mondrian 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,151 @@
1
+ require 'nokogiri'
2
+
3
+ module Mondrian
4
+ class SchemaElement
5
+ def initialize(name = nil, attributes = {}, &block)
6
+ # if just attributes hash provided
7
+ if name.is_a?(Hash) && attributes == {}
8
+ attributes = name
9
+ name = nil
10
+ end
11
+ @attributes = {}
12
+ if name
13
+ if self.class.content
14
+ @content = name
15
+ else
16
+ @attributes[:name] = name
17
+ end
18
+ end
19
+ @attributes.merge!(attributes)
20
+ self.class.elements.each do |element|
21
+ instance_variable_set("@#{pluralize(element)}", [])
22
+ end
23
+ @xml_fragments = []
24
+ instance_eval &block if block
25
+ end
26
+
27
+ def self.attributes(*names)
28
+ names.each do |name|
29
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
30
+ def #{name}(*args)
31
+ if args.empty?
32
+ @attributes[:#{name}]
33
+ elsif args.size == 1
34
+ @attributes[:#{name}] = args[0]
35
+ else
36
+ raise ArgumentError, "too many arguments"
37
+ end
38
+ end
39
+ RUBY
40
+ end
41
+ end
42
+
43
+ def self.data_dictionary_names(*names)
44
+ return @data_dictionary_names || [] if names.empty?
45
+ @data_dictionary_names ||= []
46
+ @data_dictionary_names.concat(names)
47
+ end
48
+
49
+ def self.elements(*names)
50
+ return @elements || [] if names.empty?
51
+
52
+ @elements ||= []
53
+ @elements.concat(names)
54
+
55
+ names.each do |name|
56
+ attr_reader pluralize(name).to_sym
57
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
58
+ def #{name}(name=nil, attributes = {}, &block)
59
+ @#{pluralize(name)} << Schema::#{camel_case(name)}.new(name, attributes, &block)
60
+ end
61
+ RUBY
62
+ end
63
+ end
64
+
65
+ def self.content(type=nil)
66
+ return @content if type.nil?
67
+ @content = type
68
+ end
69
+
70
+ attr_reader :xml_fragments
71
+ def xml(string)
72
+ string = string.strip
73
+ fragment = Nokogiri::XML::DocumentFragment.parse(string)
74
+ raise ArgumentError, "Invalid XML fragment:\n#{string}" if fragment.children.empty?
75
+ @xml_fragments << string
76
+ end
77
+
78
+ def to_xml(options={})
79
+ options[:upcase_data_dictionary] = @upcase_data_dictionary unless @upcase_data_dictionary.nil?
80
+ Nokogiri::XML::Builder.new do |xml|
81
+ add_to_xml(xml, options)
82
+ end.to_xml
83
+ end
84
+
85
+ protected
86
+
87
+ def add_to_xml(xml, options)
88
+ if self.class.content
89
+ xml.send(tag_name(self.class.name), @content, xmlized_attributes(options))
90
+ else
91
+ xml.send(tag_name(self.class.name), xmlized_attributes(options)) do
92
+ self.class.elements.each do |element|
93
+ instance_variable_get("@#{pluralize(element)}").each {|item| item.add_to_xml(xml, options)}
94
+ end
95
+ @xml_fragments.each do |xml_fragment|
96
+ xml.send(:insert, Nokogiri::XML::DocumentFragment.parse(xml_fragment))
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def xmlized_attributes(options)
105
+ # data dictionary values should be in uppercase if schema defined with :upcase_data_dictionary => true
106
+ # or by default when using Oracle or LucidDB driver (can be overridden by :upcase_data_dictionary => false)
107
+ upcase_attributes = if options[:upcase_data_dictionary].nil? && %w(oracle luciddb).include?(options[:driver]) ||
108
+ options[:upcase_data_dictionary]
109
+ self.class.data_dictionary_names
110
+ else
111
+ []
112
+ end
113
+ hash = {}
114
+ @attributes.each do |attr, value|
115
+ value = value.upcase if upcase_attributes.include?(attr)
116
+ hash[
117
+ # camelcase attribute name
118
+ attr.to_s.gsub(/_([^_]+)/){|m| $1.capitalize}
119
+ ] = value
120
+ end
121
+ hash
122
+ end
123
+
124
+ def self.pluralize(string)
125
+ string = string.to_s
126
+ case string
127
+ when /^(.*)y$/
128
+ "#{$1}ies"
129
+ else
130
+ "#{string}s"
131
+ end
132
+ end
133
+
134
+ def pluralize(string)
135
+ self.class.pluralize(string)
136
+ end
137
+
138
+ def self.camel_case(string)
139
+ string.to_s.split('_').map{|s| s.capitalize}.join('')
140
+ end
141
+
142
+ def camel_case(string)
143
+ self.class.camel_case(string)
144
+ end
145
+
146
+ def tag_name(string)
147
+ string.split('::').last << '_'
148
+ end
149
+ end
150
+
151
+ end
@@ -0,0 +1,3 @@
1
+ module Mondrian
2
+ VERSION = "0.0.1"
3
+ end
data/mondrian.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/mondrian/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["stellard"]
6
+ gem.email = ["scott.ellard@gmail.com"]
7
+ gem.description = %q{Schema DSL for mondrian}
8
+ gem.summary = %q{Ruby DSL for Mondrian Schema Definitions, Based on https://github.com/rsim/mondrian-olap}
9
+ gem.homepage = "https://github.com/kujilabs/mondrian"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "mondrian"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Mondrian::VERSION
17
+
18
+ gem.add_dependency 'nokogiri', '~> 1.5.0'
19
+ end
@@ -0,0 +1,642 @@
1
+ require "spec_helper"
2
+
3
+ describe Mondrian::Schema do
4
+
5
+ describe "elements" do
6
+ before(:each) do
7
+ @schema = Mondrian::Schema.new
8
+ end
9
+
10
+ describe "root element" do
11
+ it "should render to XML" do
12
+ @schema.to_xml.should be_equivalent_to <<-XML
13
+ <?xml version="1.0"?>
14
+ <Schema/>
15
+ XML
16
+ end
17
+
18
+ it "should render to XML with attributes" do
19
+ @schema.define('FoodMart') do
20
+ description 'Demo "FoodMart" schema'
21
+ end
22
+ @schema.to_xml.should be_equivalent_to <<-XML
23
+ <?xml version="1.0"?>
24
+ <Schema description="Demo &quot;FoodMart&quot; schema" name="FoodMart"/>
25
+ XML
26
+ end
27
+
28
+ it "should render to XML using class method" do
29
+ schema = Mondrian::Schema.define('FoodMart')
30
+ schema.to_xml.should be_equivalent_to <<-XML
31
+ <?xml version="1.0"?>
32
+ <Schema name="FoodMart"/>
33
+ XML
34
+ end
35
+ end
36
+
37
+ describe "Cube" do
38
+ it "should render to XML" do
39
+ @schema.define do
40
+ cube 'Sales' do
41
+ default_measure 'Unit Sales'
42
+ description 'Sales cube'
43
+ cache false
44
+ enabled true
45
+ end
46
+ end
47
+ @schema.to_xml.should be_equivalent_to <<-XML
48
+ <?xml version="1.0"?>
49
+ <Schema name="default">
50
+ <Cube cache="false" defaultMeasure="Unit Sales" description="Sales cube" enabled="true" name="Sales"/>
51
+ </Schema>
52
+ XML
53
+ end
54
+
55
+ it "should render to XML using options hash" do
56
+ @schema.define do
57
+ cube 'Sales', :default_measure => 'Unit Sales',
58
+ :description => 'Sales cube', :cache => false, :enabled => true
59
+ end
60
+ @schema.to_xml.should be_equivalent_to <<-XML
61
+ <?xml version="1.0"?>
62
+ <Schema name="default">
63
+ <Cube cache="false" defaultMeasure="Unit Sales" description="Sales cube" enabled="true" name="Sales"/>
64
+ </Schema>
65
+ XML
66
+ end
67
+ end
68
+
69
+ describe "Table" do
70
+ it "should render to XML" do
71
+ @schema.define do
72
+ cube 'Sales' do
73
+ table 'sales_fact', :alias => 'sales'
74
+ end
75
+ end
76
+ @schema.to_xml.should be_equivalent_to <<-XML
77
+ <?xml version="1.0"?>
78
+ <Schema name="default">
79
+ <Cube name="Sales">
80
+ <Table alias="sales" name="sales_fact"/>
81
+ </Cube>
82
+ </Schema>
83
+ XML
84
+ end
85
+
86
+ it "should render table name in uppercase when using Oracle or LucidDB driver" do
87
+ @schema.define do
88
+ cube 'Sales' do
89
+ table 'sales_fact', :alias => 'sales', :schema => 'facts'
90
+ end
91
+ end
92
+ %w(oracle luciddb).each do |driver|
93
+ @schema.to_xml(:driver => driver).should be_equivalent_to <<-XML
94
+ <?xml version="1.0"?>
95
+ <Schema name="default">
96
+ <Cube name="Sales">
97
+ <Table alias="SALES" name="SALES_FACT" schema="FACTS"/>
98
+ </Cube>
99
+ </Schema>
100
+ XML
101
+ end
102
+ end
103
+
104
+ it "should render table name in uppercase when :upcase_data_dictionary option is set to true" do
105
+ @schema.define :upcase_data_dictionary => true do
106
+ cube 'Sales' do
107
+ table 'sales_fact', :alias => 'sales', :schema => 'facts'
108
+ end
109
+ end
110
+ @schema.to_xml.should be_equivalent_to <<-XML
111
+ <?xml version="1.0"?>
112
+ <Schema name="default">
113
+ <Cube name="Sales">
114
+ <Table alias="SALES" name="SALES_FACT" schema="FACTS"/>
115
+ </Cube>
116
+ </Schema>
117
+ XML
118
+ end
119
+
120
+ it "should render table name in lowercase when using Oracle or LucidDB driver but with :upcase_data_dictionary set to false" do
121
+ @schema.define :upcase_data_dictionary => false do
122
+ cube 'Sales' do
123
+ table 'sales_fact', :alias => 'sales', :schema => 'facts'
124
+ end
125
+ end
126
+ %w(oracle luciddb).each do |driver|
127
+ @schema.to_xml(:driver => driver).should be_equivalent_to <<-XML
128
+ <?xml version="1.0"?>
129
+ <Schema name="default">
130
+ <Cube name="Sales">
131
+ <Table alias="sales" name="sales_fact" schema="facts"/>
132
+ </Cube>
133
+ </Schema>
134
+ XML
135
+ end
136
+ end
137
+
138
+ it "should render table with where condition" do
139
+ @schema.define do
140
+ cube 'Sales' do
141
+ table 'sales_fact', :alias => 'sales' do
142
+ sql 'customer_id IS NOT NULL'
143
+ end
144
+ end
145
+ end
146
+ @schema.to_xml.should be_equivalent_to <<-XML
147
+ <?xml version="1.0"?>
148
+ <Schema name="default">
149
+ <Cube name="Sales">
150
+ <Table alias="sales" name="sales_fact">
151
+ <SQL>customer_id IS NOT NULL</SQL>
152
+ </Table>
153
+ </Cube>
154
+ </Schema>
155
+ XML
156
+ end
157
+ end
158
+
159
+ describe "View" do
160
+ it "should render to XML" do
161
+ @schema.define do
162
+ cube 'Sales' do
163
+ view :alias => 'sales' do
164
+ sql 'select * from sales_fact'
165
+ end
166
+ end
167
+ end
168
+ @schema.to_xml.should be_equivalent_to <<-XML
169
+ <?xml version="1.0"?>
170
+ <Schema name="default">
171
+ <Cube name="Sales">
172
+ <View alias="sales">
173
+ <SQL>select * from sales_fact</SQL>
174
+ </View>
175
+ </Cube>
176
+ </Schema>
177
+ XML
178
+ end
179
+ end
180
+
181
+ describe "Dimension" do
182
+ it "should render to XML" do
183
+ @schema.define do
184
+ cube 'Sales' do
185
+ dimension 'Gender' do
186
+ foreign_key 'customer_id'
187
+ hierarchy do
188
+ has_all true
189
+ all_member_name 'All Genders'
190
+ primary_key 'customer_id'
191
+ table 'customer'
192
+ level 'Gender', :column => 'gender', :unique_members => true
193
+ end
194
+ end
195
+ end
196
+ end
197
+ @schema.to_xml.should be_equivalent_to <<-XML
198
+ <?xml version="1.0"?>
199
+ <Schema name="default">
200
+ <Cube name="Sales">
201
+ <Dimension foreignKey="customer_id" name="Gender">
202
+ <Hierarchy allMemberName="All Genders" hasAll="true" primaryKey="customer_id">
203
+ <Table name="customer"/>
204
+ <Level column="gender" name="Gender" uniqueMembers="true"/>
205
+ </Hierarchy>
206
+ </Dimension>
207
+ </Cube>
208
+ </Schema>
209
+ XML
210
+ end
211
+
212
+ it "should render time dimension" do
213
+ @schema.define do
214
+ cube 'Sales' do
215
+ dimension 'Time' do
216
+ foreign_key 'time_id'
217
+ hierarchy do
218
+ has_all false
219
+ primary_key 'time_id'
220
+ table 'time_by_day'
221
+ level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true
222
+ level 'Quarter', :column => 'quarter', :unique_members => false
223
+ level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false
224
+ end
225
+ end
226
+ end
227
+ end
228
+ @schema.to_xml.should be_equivalent_to <<-XML
229
+ <?xml version="1.0"?>
230
+ <Schema name="default">
231
+ <Cube name="Sales">
232
+ <Dimension foreignKey="time_id" name="Time">
233
+ <Hierarchy hasAll="false" primaryKey="time_id">
234
+ <Table name="time_by_day"/>
235
+ <Level column="the_year" name="Year" type="Numeric" uniqueMembers="true"/>
236
+ <Level column="quarter" name="Quarter" uniqueMembers="false"/>
237
+ <Level column="month_of_year" name="Month" type="Numeric" uniqueMembers="false"/>
238
+ </Hierarchy>
239
+ </Dimension>
240
+ </Cube>
241
+ </Schema>
242
+ XML
243
+ end
244
+
245
+ it "should render dimension hierarchy with join" do
246
+ @schema.define do
247
+ cube 'Sales' do
248
+ dimension 'Products', :foreign_key => 'product_id' do
249
+ hierarchy :has_all => true, :all_member_name => 'All Products',
250
+ :primary_key => 'product_id', :primary_key_table => 'product' do
251
+ join :left_key => 'product_class_id', :right_key => 'product_class_id' do
252
+ table 'product'
253
+ table 'product_class'
254
+ end
255
+ level 'Product Family', :table => 'product_class', :column => 'product_family', :unique_members => true
256
+ level 'Brand Name', :table => 'product', :column => 'brand_name', :unique_members => false
257
+ level 'Product Name', :table => 'product', :column => 'product_name', :unique_members => true
258
+ end
259
+ end
260
+ end
261
+ end
262
+ @schema.to_xml.should be_equivalent_to <<-XML
263
+ <?xml version="1.0"?>
264
+ <Schema name="default">
265
+ <Cube name="Sales">
266
+ <Dimension foreignKey="product_id" name="Products">
267
+ <Hierarchy allMemberName="All Products" hasAll="true" primaryKey="product_id" primaryKeyTable="product">
268
+ <Join leftKey="product_class_id" rightKey="product_class_id">
269
+ <Table name="product"/>
270
+ <Table name="product_class"/>
271
+ </Join>
272
+ <Level column="product_family" name="Product Family" table="product_class" uniqueMembers="true"/>
273
+ <Level column="brand_name" name="Brand Name" table="product" uniqueMembers="false"/>
274
+ <Level column="product_name" name="Product Name" table="product" uniqueMembers="true"/>
275
+ </Hierarchy>
276
+ </Dimension>
277
+ </Cube>
278
+ </Schema>
279
+ XML
280
+ end
281
+
282
+ it "should render table and column names in uppercase when using Oracle driver" do
283
+ @schema.define do
284
+ cube 'Sales' do
285
+ dimension 'Products', :foreign_key => 'product_id' do
286
+ hierarchy :has_all => true, :all_member_name => 'All Products',
287
+ :primary_key => 'product_id', :primary_key_table => 'product' do
288
+ join :left_key => 'product_class_id', :right_key => 'product_class_id' do
289
+ table 'product'
290
+ table 'product_class'
291
+ end
292
+ level 'Product Family', :table => 'product_class', :column => 'product_family', :unique_members => true
293
+ level 'Brand Name', :table => 'product', :column => 'brand_name', :unique_members => false
294
+ level 'Product Name', :table => 'product', :column => 'product_name', :unique_members => true
295
+ end
296
+ end
297
+ end
298
+ end
299
+ @schema.to_xml(:driver => 'oracle').should be_equivalent_to <<-XML
300
+ <?xml version="1.0"?>
301
+ <Schema name="default">
302
+ <Cube name="Sales">
303
+ <Dimension foreignKey="PRODUCT_ID" name="Products">
304
+ <Hierarchy allMemberName="All Products" hasAll="true" primaryKey="PRODUCT_ID" primaryKeyTable="PRODUCT">
305
+ <Join leftKey="PRODUCT_CLASS_ID" rightKey="PRODUCT_CLASS_ID">
306
+ <Table name="PRODUCT"/>
307
+ <Table name="PRODUCT_CLASS"/>
308
+ </Join>
309
+ <Level column="PRODUCT_FAMILY" name="Product Family" table="PRODUCT_CLASS" uniqueMembers="true"/>
310
+ <Level column="BRAND_NAME" name="Brand Name" table="PRODUCT" uniqueMembers="false"/>
311
+ <Level column="PRODUCT_NAME" name="Product Name" table="PRODUCT" uniqueMembers="true"/>
312
+ </Hierarchy>
313
+ </Dimension>
314
+ </Cube>
315
+ </Schema>
316
+ XML
317
+ end
318
+
319
+ it "should render dimension hierarchy with nested joins" do
320
+ @schema.define do
321
+ cube 'Sales' do
322
+ dimension 'Products', :foreign_key => 'product_id' do
323
+ hierarchy :has_all => true, :all_member_name => 'All Products',
324
+ :primary_key => 'product_id', :primary_key_table => 'product' do
325
+ join :left_key => 'product_class_id', :right_alias => 'product_class', :right_key => 'product_class_id' do
326
+ table 'product'
327
+ join :left_key => 'product_type_id', :right_key => 'product_type_id' do
328
+ table 'product_class'
329
+ table 'product_type'
330
+ end
331
+ end
332
+ level 'Product Family', :table => 'product_type', :column => 'product_family', :unique_members => true
333
+ level 'Product Category', :table => 'product_class', :column => 'product_category', :unique_members => false
334
+ level 'Brand Name', :table => 'product', :column => 'brand_name', :unique_members => false
335
+ level 'Product Name', :table => 'product', :column => 'product_name', :unique_members => true
336
+ end
337
+ end
338
+ end
339
+ end
340
+ @schema.to_xml.should be_equivalent_to <<-XML
341
+ <?xml version="1.0"?>
342
+ <Schema name="default">
343
+ <Cube name="Sales">
344
+ <Dimension foreignKey="product_id" name="Products">
345
+ <Hierarchy allMemberName="All Products" hasAll="true" primaryKey="product_id" primaryKeyTable="product">
346
+ <Join leftKey="product_class_id" rightAlias="product_class" rightKey="product_class_id">
347
+ <Table name="product"/>
348
+ <Join leftKey="product_type_id" rightKey="product_type_id">
349
+ <Table name="product_class"/>
350
+ <Table name="product_type"/>
351
+ </Join>
352
+ </Join>
353
+ <Level column="product_family" name="Product Family" table="product_type" uniqueMembers="true"/>
354
+ <Level column="product_category" name="Product Category" table="product_class" uniqueMembers="false"/>
355
+ <Level column="brand_name" name="Brand Name" table="product" uniqueMembers="false"/>
356
+ <Level column="product_name" name="Product Name" table="product" uniqueMembers="true"/>
357
+ </Hierarchy>
358
+ </Dimension>
359
+ </Cube>
360
+ </Schema>
361
+ XML
362
+ end
363
+
364
+ end
365
+
366
+ describe "Measure" do
367
+ it "should render XML" do
368
+ @schema.define do
369
+ cube 'Sales' do
370
+ measure 'Unit Sales' do
371
+ column 'unit_sales'
372
+ aggregator 'sum'
373
+ end
374
+ end
375
+ end
376
+ @schema.to_xml.should be_equivalent_to <<-XML
377
+ <?xml version="1.0"?>
378
+ <Schema name="default">
379
+ <Cube name="Sales">
380
+ <Measure aggregator="sum" column="unit_sales" name="Unit Sales"/>
381
+ </Cube>
382
+ </Schema>
383
+ XML
384
+ end
385
+
386
+ it "should render column name in uppercase when using Oracle driver" do
387
+ @schema.define do
388
+ cube 'Sales' do
389
+ measure 'Unit Sales' do
390
+ column 'unit_sales'
391
+ aggregator 'sum'
392
+ end
393
+ end
394
+ end
395
+ @schema.to_xml(:driver => 'oracle').should be_equivalent_to <<-XML
396
+ <?xml version="1.0"?>
397
+ <Schema name="default">
398
+ <Cube name="Sales">
399
+ <Measure aggregator="sum" column="UNIT_SALES" name="Unit Sales"/>
400
+ </Cube>
401
+ </Schema>
402
+ XML
403
+ end
404
+
405
+ it "should render with measure expression" do
406
+ @schema.define do
407
+ cube 'Sales' do
408
+ measure 'Double Unit Sales', :aggregator => 'sum' do
409
+ measure_expression do
410
+ sql 'unit_sales * 2'
411
+ end
412
+ end
413
+ end
414
+ end
415
+ @schema.to_xml.should be_equivalent_to <<-XML
416
+ <?xml version="1.0"?>
417
+ <Schema name="default">
418
+ <Cube name="Sales">
419
+ <Measure aggregator="sum" name="Double Unit Sales">
420
+ <MeasureExpression>
421
+ <SQL>unit_sales * 2</SQL>
422
+ </MeasureExpression>
423
+ </Measure>
424
+ </Cube>
425
+ </Schema>
426
+ XML
427
+ end
428
+ end
429
+
430
+ describe "Calculated Member" do
431
+ it "should render XML" do
432
+ @schema.define do
433
+ cube 'Sales' do
434
+ calculated_member 'Profit' do
435
+ dimension 'Measures'
436
+ formula '[Measures].[Store Sales] - [Measures].[Store Cost]'
437
+ format_string '#,##0.00'
438
+ end
439
+ end
440
+ end
441
+ @schema.to_xml.should be_equivalent_to <<-XML
442
+ <?xml version="1.0"?>
443
+ <Schema name="default">
444
+ <Cube name="Sales">
445
+ <CalculatedMember dimension="Measures" formatString="#,##0.00" name="Profit">
446
+ <Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>
447
+ </CalculatedMember>
448
+ </Cube>
449
+ </Schema>
450
+ XML
451
+ end
452
+ end
453
+
454
+ describe "Aggregates" do
455
+ it "should render named aggregate to XML" do
456
+ @schema.define do
457
+ cube 'Sales' do
458
+ table 'sales_fact_1997' do
459
+ agg_name 'agg_c_special_sales_fact_1997' do
460
+ agg_fact_count :column => 'fact_count'
461
+ agg_measure '[Measures].[Store Cost]', :column => 'store_cost_sum'
462
+ agg_measure '[Measures].[Store Sales]', :column => 'store_sales_sum'
463
+ agg_level '[Product].[Product Family]', :column => 'product_family'
464
+ agg_level '[Time].[Quarter]', :column => 'time_quarter'
465
+ agg_level '[Time].[Year]', :column => 'time_year'
466
+ agg_level '[Time].[Quarter]', :column => 'time_quarter'
467
+ agg_level '[Time].[Month]', :column => 'time_month'
468
+ end
469
+ end
470
+ end
471
+ end
472
+ @schema.to_xml.should be_equivalent_to <<-XML
473
+ <?xml version="1.0"?>
474
+ <Schema name="default">
475
+ <Cube name="Sales">
476
+ <Table name="sales_fact_1997">
477
+ <AggName name="agg_c_special_sales_fact_1997">
478
+ <AggFactCount column="fact_count"/>
479
+ <AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
480
+ <AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
481
+ <AggLevel column="product_family" name="[Product].[Product Family]"/>
482
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
483
+ <AggLevel column="time_year" name="[Time].[Year]"/>
484
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
485
+ <AggLevel column="time_month" name="[Time].[Month]"/>
486
+ </AggName>
487
+ </Table>
488
+ </Cube>
489
+ </Schema>
490
+ XML
491
+ end
492
+
493
+ it "should render aggregate pattern to XML" do
494
+ @schema.define do
495
+ cube 'Sales' do
496
+ table 'sales_fact_1997' do
497
+ agg_pattern :pattern => 'agg_.*_sales_fact_1997' do
498
+ agg_fact_count :column => 'fact_count'
499
+ agg_measure '[Measures].[Store Cost]', :column => 'store_cost_sum'
500
+ agg_measure '[Measures].[Store Sales]', :column => 'store_sales_sum'
501
+ agg_level '[Product].[Product Family]', :column => 'product_family'
502
+ agg_level '[Time].[Quarter]', :column => 'time_quarter'
503
+ agg_level '[Time].[Year]', :column => 'time_year'
504
+ agg_level '[Time].[Quarter]', :column => 'time_quarter'
505
+ agg_level '[Time].[Month]', :column => 'time_month'
506
+ agg_exclude 'agg_c_14_sales_fact_1997'
507
+ agg_exclude 'agg_lc_100_sales_fact_1997'
508
+ end
509
+ end
510
+ end
511
+ end
512
+ @schema.to_xml.should be_equivalent_to <<-XML
513
+ <?xml version="1.0"?>
514
+ <Schema name="default">
515
+ <Cube name="Sales">
516
+ <Table name="sales_fact_1997">
517
+ <AggPattern pattern="agg_.*_sales_fact_1997">
518
+ <AggFactCount column="fact_count"/>
519
+ <AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
520
+ <AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
521
+ <AggLevel column="product_family" name="[Product].[Product Family]"/>
522
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
523
+ <AggLevel column="time_year" name="[Time].[Year]"/>
524
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
525
+ <AggLevel column="time_month" name="[Time].[Month]"/>
526
+ <AggExclude name="agg_c_14_sales_fact_1997"/>
527
+ <AggExclude name="agg_lc_100_sales_fact_1997"/>
528
+ </AggPattern>
529
+ </Table>
530
+ </Cube>
531
+ </Schema>
532
+ XML
533
+ end
534
+
535
+ it "should render embedded aggregate XML defintion to XML" do
536
+ @schema.define do
537
+ cube 'Sales' do
538
+ table 'sales_fact_1997' do
539
+ xml <<-XML
540
+ <AggName name="agg_c_special_sales_fact_1997">
541
+ <AggFactCount column="fact_count"/>
542
+ <AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
543
+ <AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
544
+ <AggLevel column="product_family" name="[Product].[Product Family]"/>
545
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
546
+ <AggLevel column="time_year" name="[Time].[Year]"/>
547
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
548
+ <AggLevel column="time_month" name="[Time].[Month]"/>
549
+ </AggName>
550
+ <AggPattern pattern="agg_.*_sales_fact_1997">
551
+ <AggFactCount column="fact_count"/>
552
+ <AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
553
+ <AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
554
+ <AggLevel column="product_family" name="[Product].[Product Family]"/>
555
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
556
+ <AggLevel column="time_year" name="[Time].[Year]"/>
557
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
558
+ <AggLevel column="time_month" name="[Time].[Month]"/>
559
+ <AggExclude name="agg_c_14_sales_fact_1997"/>
560
+ <AggExclude name="agg_lc_100_sales_fact_1997"/>
561
+ </AggPattern>
562
+ XML
563
+ end
564
+ end
565
+ end
566
+ @schema.to_xml.should be_equivalent_to <<-XML
567
+ <?xml version="1.0"?>
568
+ <Schema name="default">
569
+ <Cube name="Sales">
570
+ <Table name="sales_fact_1997">
571
+ <AggName name="agg_c_special_sales_fact_1997">
572
+ <AggFactCount column="fact_count"/>
573
+ <AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
574
+ <AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
575
+ <AggLevel column="product_family" name="[Product].[Product Family]"/>
576
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
577
+ <AggLevel column="time_year" name="[Time].[Year]"/>
578
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
579
+ <AggLevel column="time_month" name="[Time].[Month]"/>
580
+ </AggName>
581
+ <AggPattern pattern="agg_.*_sales_fact_1997">
582
+ <AggFactCount column="fact_count"/>
583
+ <AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
584
+ <AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
585
+ <AggLevel column="product_family" name="[Product].[Product Family]"/>
586
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
587
+ <AggLevel column="time_year" name="[Time].[Year]"/>
588
+ <AggLevel column="time_quarter" name="[Time].[Quarter]"/>
589
+ <AggLevel column="time_month" name="[Time].[Month]"/>
590
+ <AggExclude name="agg_c_14_sales_fact_1997"/>
591
+ <AggExclude name="agg_lc_100_sales_fact_1997"/>
592
+ </AggPattern>
593
+ </Table>
594
+ </Cube>
595
+ </Schema>
596
+ XML
597
+ end
598
+
599
+ end
600
+
601
+ describe "Member properties" do
602
+ it "should render XML" do
603
+ @schema.define do
604
+ cube 'Sales' do
605
+ dimension 'Employees', :foreign_key => 'employee_id' do
606
+ hierarchy :has_all => true, :all_member_name => 'All Employees', :primary_key => 'employee_id' do
607
+ table 'employee'
608
+ level 'Employee Id', :unique_members => true, :type => 'Numeric', :column => 'employee_id', :name_column => 'full_name',
609
+ :parent_column => 'supervisor_id', :null_parent_value => 0 do
610
+ property 'Marital Status', :column => 'marital_status'
611
+ property 'Position Title', :column => 'position_title'
612
+ property 'Gender', :column => 'gender'
613
+ property 'Salary', :column => 'salary'
614
+ property 'Education Level', :column => 'education_level'
615
+ end
616
+ end
617
+ end
618
+ end
619
+ end
620
+ @schema.to_xml.should be_equivalent_to <<-XML
621
+ <?xml version="1.0"?>
622
+ <Schema name="default">
623
+ <Cube name="Sales">
624
+ <Dimension foreignKey="employee_id" name="Employees">
625
+ <Hierarchy allMemberName="All Employees" hasAll="true" primaryKey="employee_id">
626
+ <Table name="employee"/>
627
+ <Level column="employee_id" name="Employee Id" nameColumn="full_name" nullParentValue="0" parentColumn="supervisor_id" type="Numeric" uniqueMembers="true">
628
+ <Property column="marital_status" name="Marital Status"/>
629
+ <Property column="position_title" name="Position Title"/>
630
+ <Property column="gender" name="Gender"/>
631
+ <Property column="salary" name="Salary"/>
632
+ <Property column="education_level" name="Education Level"/>
633
+ </Level>
634
+ </Hierarchy>
635
+ </Dimension>
636
+ </Cube>
637
+ </Schema>
638
+ XML
639
+ end
640
+ end
641
+ end
642
+ end