mondrian 0.0.1

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.
@@ -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