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.
- checksums.yaml +7 -0
- data/Changelog.md +38 -0
- data/LICENSE.txt +1 -1
- data/README.md +302 -0
- data/VERSION +1 -1
- data/lib/mondrian/jars/commons-collections-3.2.jar +0 -0
- data/lib/mondrian/jars/commons-logging-1.1.1.jar +0 -0
- data/lib/mondrian/jars/commons-math-1.1.jar +0 -0
- data/lib/mondrian/jars/eigenbase-properties-1.1.2.jar +0 -0
- data/lib/mondrian/jars/eigenbase-resgen-1.3.1.jar +0 -0
- data/lib/mondrian/jars/eigenbase-xom-1.3.1.jar +0 -0
- data/lib/mondrian/jars/{javacup.jar → javacup-10k.jar} +0 -0
- data/lib/mondrian/jars/log4j-1.2.14.jar +0 -0
- data/lib/mondrian/jars/mondrian.jar +0 -0
- data/lib/mondrian/jars/olap4j-1.0.1.539.jar +0 -0
- data/lib/mondrian/olap.rb +2 -1
- data/lib/mondrian/olap/connection.rb +163 -32
- data/lib/mondrian/olap/cube.rb +163 -24
- data/lib/mondrian/olap/error.rb +57 -0
- data/lib/mondrian/olap/query.rb +52 -17
- data/lib/mondrian/olap/result.rb +298 -6
- data/lib/mondrian/olap/schema.rb +220 -29
- data/lib/mondrian/olap/schema_element.rb +31 -11
- data/lib/mondrian/olap/schema_udf.rb +331 -0
- data/lib/mondrian/olap/version.rb +5 -0
- data/spec/connection_role_spec.rb +130 -0
- data/spec/connection_spec.rb +36 -1
- data/spec/cube_spec.rb +137 -7
- data/spec/fixtures/MondrianTest.xml +4 -4
- data/spec/mondrian_spec.rb +53 -0
- data/spec/query_spec.rb +294 -11
- data/spec/rake_tasks.rb +8 -8
- data/spec/schema_definition_spec.rb +845 -26
- data/spec/spec_helper.rb +26 -17
- data/spec/support/matchers/be_like.rb +2 -2
- metadata +296 -237
- data/.rspec +0 -2
- data/Gemfile +0 -18
- data/README.rdoc +0 -221
- data/RUNNING_TESTS.rdoc +0 -66
- data/Rakefile +0 -46
- data/lib/mondrian/jars/commons-collections-3.1.jar +0 -0
- data/lib/mondrian/jars/commons-logging-1.0.4.jar +0 -0
- data/lib/mondrian/jars/commons-math-1.0.jar +0 -0
- data/lib/mondrian/jars/eigenbase-properties.jar +0 -0
- data/lib/mondrian/jars/eigenbase-resgen.jar +0 -0
- data/lib/mondrian/jars/eigenbase-xom.jar +0 -0
- data/lib/mondrian/jars/log4j-1.2.8.jar +0 -0
- data/lib/mondrian/jars/olap4j.jar +0 -0
- data/mondrian-olap.gemspec +0 -126
@@ -1,9 +1,7 @@
|
|
1
|
-
require 'nokogiri'
|
2
|
-
|
3
1
|
module Mondrian
|
4
2
|
module OLAP
|
5
3
|
class SchemaElement
|
6
|
-
def initialize(name = nil, attributes = {}, &block)
|
4
|
+
def initialize(name = nil, attributes = {}, parent = nil, &block)
|
7
5
|
# if just attributes hash provided
|
8
6
|
if name.is_a?(Hash) && attributes == {}
|
9
7
|
attributes = name
|
@@ -12,7 +10,14 @@ module Mondrian
|
|
12
10
|
@attributes = {}
|
13
11
|
if name
|
14
12
|
if self.class.content
|
15
|
-
|
13
|
+
if attributes.is_a?(Hash)
|
14
|
+
@content = name
|
15
|
+
else
|
16
|
+
# used for Annotation element where both name and content is given as arguments
|
17
|
+
@attributes[:name] = name
|
18
|
+
@content = attributes
|
19
|
+
attributes = {}
|
20
|
+
end
|
16
21
|
else
|
17
22
|
@attributes[:name] = name
|
18
23
|
end
|
@@ -21,8 +26,12 @@ module Mondrian
|
|
21
26
|
self.class.elements.each do |element|
|
22
27
|
instance_variable_set("@#{pluralize(element)}", [])
|
23
28
|
end
|
29
|
+
# extract annotations from options
|
30
|
+
if @attributes[:annotations] && self.class.elements.include?(:annotations)
|
31
|
+
annotations @attributes.delete(:annotations)
|
32
|
+
end
|
24
33
|
@xml_fragments = []
|
25
|
-
instance_eval
|
34
|
+
instance_eval(&block) if block
|
26
35
|
end
|
27
36
|
|
28
37
|
def self.attributes(*names)
|
@@ -54,10 +63,11 @@ module Mondrian
|
|
54
63
|
@elements.concat(names)
|
55
64
|
|
56
65
|
names.each do |name|
|
66
|
+
next if name == :xml
|
57
67
|
attr_reader pluralize(name).to_sym
|
58
68
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
59
69
|
def #{name}(name=nil, attributes = {}, &block)
|
60
|
-
@#{pluralize(name)} << Schema::#{camel_case(name)}.new(name, attributes, &block)
|
70
|
+
@#{pluralize(name)} << Schema::#{camel_case(name)}.new(name, attributes, self, &block)
|
61
71
|
end
|
62
72
|
RUBY
|
63
73
|
end
|
@@ -78,7 +88,7 @@ module Mondrian
|
|
78
88
|
|
79
89
|
def to_xml(options={})
|
80
90
|
options[:upcase_data_dictionary] = @upcase_data_dictionary unless @upcase_data_dictionary.nil?
|
81
|
-
Nokogiri::XML::Builder.new do |xml|
|
91
|
+
Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
82
92
|
add_to_xml(xml, options)
|
83
93
|
end.to_xml
|
84
94
|
end
|
@@ -90,16 +100,26 @@ module Mondrian
|
|
90
100
|
xml.send(tag_name(self.class.name), @content, xmlized_attributes(options))
|
91
101
|
else
|
92
102
|
xml.send(tag_name(self.class.name), xmlized_attributes(options)) do
|
103
|
+
xml_fragments_added = false
|
93
104
|
self.class.elements.each do |element|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
105
|
+
if element == :xml
|
106
|
+
add_xml_fragments(xml)
|
107
|
+
xml_fragments_added = true
|
108
|
+
else
|
109
|
+
instance_variable_get("@#{pluralize(element)}").each {|item| item.add_to_xml(xml, options)}
|
110
|
+
end
|
98
111
|
end
|
112
|
+
add_xml_fragments(xml) unless xml_fragments_added
|
99
113
|
end
|
100
114
|
end
|
101
115
|
end
|
102
116
|
|
117
|
+
def add_xml_fragments(xml)
|
118
|
+
@xml_fragments.each do |xml_fragment|
|
119
|
+
xml.send(:insert, Nokogiri::XML::DocumentFragment.parse(xml_fragment))
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
103
123
|
private
|
104
124
|
|
105
125
|
def xmlized_attributes(options)
|
@@ -0,0 +1,331 @@
|
|
1
|
+
require 'jruby/core_ext'
|
2
|
+
|
3
|
+
module Mondrian
|
4
|
+
module OLAP
|
5
|
+
class Schema < SchemaElement
|
6
|
+
|
7
|
+
def user_defined_cell_formatter(name, &block)
|
8
|
+
CellFormatter.new(name, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ScriptElements
|
12
|
+
def javascript(text)
|
13
|
+
script text, :language => 'JavaScript'
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def coffeescript_function(arguments_string, text)
|
19
|
+
# construct function to ensure that last expression is returned
|
20
|
+
coffee_text = "#{arguments_string} ->\n" << text.gsub(/^/,' ')
|
21
|
+
javascript_text = CoffeeScript.compile(coffee_text, :bare => true)
|
22
|
+
# remove function definition first and last lines
|
23
|
+
javascript_text = javascript_text.strip.lines.to_a[1..-2].join
|
24
|
+
javascript javascript_text
|
25
|
+
end
|
26
|
+
|
27
|
+
def ruby(*options, &block)
|
28
|
+
udf_class_name = if options.include?(:shared)
|
29
|
+
"#{name.capitalize}Udf"
|
30
|
+
end
|
31
|
+
if udf_class_name && self.class.const_defined?(udf_class_name)
|
32
|
+
udf_class = self.class.const_get(udf_class_name)
|
33
|
+
else
|
34
|
+
udf_class = Class.new(RubyUdfBase)
|
35
|
+
self.class.const_set(udf_class_name, udf_class) if udf_class_name
|
36
|
+
end
|
37
|
+
udf_class.function_name = name
|
38
|
+
udf_class.class_eval(&block)
|
39
|
+
udf_java_class = udf_class.become_java!(false)
|
40
|
+
|
41
|
+
class_name udf_java_class.getName
|
42
|
+
end
|
43
|
+
|
44
|
+
def ruby_formatter(options, interface_class, method, signature, &block)
|
45
|
+
formatter_class_name = if options.include?(:shared) && @attributes[:name]
|
46
|
+
ruby_formatter_name_to_class_name(@attributes[:name])
|
47
|
+
end
|
48
|
+
if formatter_class_name && self.class.const_defined?(formatter_class_name)
|
49
|
+
formatter_class = self.class.const_get(formatter_class_name)
|
50
|
+
else
|
51
|
+
formatter_class = Class.new
|
52
|
+
self.class.const_set(formatter_class_name, formatter_class) if formatter_class_name
|
53
|
+
end
|
54
|
+
|
55
|
+
formatter_class.class_eval do
|
56
|
+
include interface_class
|
57
|
+
define_method method, &block
|
58
|
+
add_method_signature(method, signature)
|
59
|
+
end
|
60
|
+
formatter_java_class = formatter_class.become_java!(false)
|
61
|
+
class_name formatter_java_class.getName
|
62
|
+
end
|
63
|
+
|
64
|
+
def ruby_formatter_name_to_class_name(name)
|
65
|
+
# upcase just first character
|
66
|
+
"#{name.sub(/\A./){|m| m.upcase}}Udf"
|
67
|
+
end
|
68
|
+
|
69
|
+
def ruby_formatter_java_class_name(name)
|
70
|
+
"rubyobj.#{self.class.name.gsub('::','.')}.#{ruby_formatter_name_to_class_name(name)}"
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
class UserDefinedFunction < SchemaElement
|
76
|
+
include ScriptElements
|
77
|
+
attributes :name, # Name with which the user-defined function will be referenced in MDX expressions.
|
78
|
+
# Name of the class which implemenets this user-defined function.
|
79
|
+
# Must implement the mondrian.spi.UserDefinedFunction interface.
|
80
|
+
:class_name
|
81
|
+
elements :script
|
82
|
+
|
83
|
+
def coffeescript(text)
|
84
|
+
coffee_text = "__udf__ = {\n" << text << "}\n"
|
85
|
+
javascript_text = CoffeeScript.compile(coffee_text, :bare => true)
|
86
|
+
javascript_text << <<-JS
|
87
|
+
|
88
|
+
__udf__.parameters || (__udf__.parameters = []);
|
89
|
+
__udf__.returns || (__udf__.returns = "Scalar");
|
90
|
+
var __scalarTypes__ = {"Numeric":true,"String":true,"Boolean":true,"DateTime":true,"Decimal":true,"Scalar":true};
|
91
|
+
function __getType__(type) {
|
92
|
+
if (__scalarTypes__[type]) {
|
93
|
+
return new mondrian.olap.type[type+"Type"];
|
94
|
+
} else if (type === "Member") {
|
95
|
+
return mondrian.olap.type.MemberType.Unknown;
|
96
|
+
} else {
|
97
|
+
return null;
|
98
|
+
}
|
99
|
+
}
|
100
|
+
function getParameterTypes() {
|
101
|
+
var parameters = __udf__.parameters || [],
|
102
|
+
types = [];
|
103
|
+
for (var i = 0, len = parameters.length; i < len; i++) {
|
104
|
+
types.push(__getType__(parameters[i]))
|
105
|
+
}
|
106
|
+
return types;
|
107
|
+
}
|
108
|
+
function getReturnType(parameterTypes) {
|
109
|
+
var returns = __udf__.returns || "Scalar";
|
110
|
+
return __getType__(returns);
|
111
|
+
}
|
112
|
+
if (__udf__.syntax) {
|
113
|
+
function getSyntax() {
|
114
|
+
return mondrian.olap.Syntax[__udf__.syntax];
|
115
|
+
}
|
116
|
+
}
|
117
|
+
function execute(evaluator, args) {
|
118
|
+
var parameters = __udf__.parameters || [],
|
119
|
+
values = [],
|
120
|
+
value;
|
121
|
+
for (var i = 0, len = parameters.length; i < len; i++) {
|
122
|
+
if (__scalarTypes__[parameters[i]]) {
|
123
|
+
value = args[i].evaluateScalar(evaluator);
|
124
|
+
} else {
|
125
|
+
value = args[i].evaluate(evaluator);
|
126
|
+
}
|
127
|
+
values.push(value);
|
128
|
+
}
|
129
|
+
return __udf__.execute.apply(__udf__, values);
|
130
|
+
}
|
131
|
+
JS
|
132
|
+
javascript javascript_text
|
133
|
+
end
|
134
|
+
|
135
|
+
class RubyUdfBase
|
136
|
+
include Java::mondrian.spi.UserDefinedFunction
|
137
|
+
def self.function_name=(name); @function_name = name; end
|
138
|
+
def self.function_name; @function_name; end
|
139
|
+
|
140
|
+
def getName
|
141
|
+
self.class.function_name
|
142
|
+
end
|
143
|
+
add_method_signature("getName", [java.lang.String])
|
144
|
+
|
145
|
+
def getDescription
|
146
|
+
getName
|
147
|
+
end
|
148
|
+
add_method_signature("getDescription", [java.lang.String])
|
149
|
+
|
150
|
+
def self.parameters(*types)
|
151
|
+
if types.empty?
|
152
|
+
@parameters || []
|
153
|
+
else
|
154
|
+
@parameters = types.map{|type| stringified_type(type)}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.returns(type = nil)
|
159
|
+
if type
|
160
|
+
@returns = stringified_type(type)
|
161
|
+
else
|
162
|
+
@returns || 'Scalar'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
VALID_SYNTAX_TYPES = %w(Function Property Method)
|
167
|
+
def self.syntax(type = nil)
|
168
|
+
if type
|
169
|
+
type = stringify(type)
|
170
|
+
raise ArgumentError, "invalid user defined function type #{type.inspect}" unless VALID_SYNTAX_TYPES.include? type
|
171
|
+
@syntax = type
|
172
|
+
else
|
173
|
+
@syntax || 'Function'
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def getSyntax
|
178
|
+
Java::mondrian.olap.Syntax.const_get self.class.syntax
|
179
|
+
end
|
180
|
+
add_method_signature("getSyntax", [Java::mondrian.olap.Syntax])
|
181
|
+
|
182
|
+
UDF_SCALAR_TYPES = {
|
183
|
+
"Numeric" => Java::mondrian.olap.type.NumericType,
|
184
|
+
"String" => Java::mondrian.olap.type.StringType,
|
185
|
+
"Boolean" => Java::mondrian.olap.type.BooleanType,
|
186
|
+
"DateTime" => Java::mondrian.olap.type.DateTimeType,
|
187
|
+
"Decimal" => Java::mondrian.olap.type.DecimalType,
|
188
|
+
"Scalar" => Java::mondrian.olap.type.ScalarType
|
189
|
+
}
|
190
|
+
UDF_OTHER_TYPES = {
|
191
|
+
"Member" => Java::mondrian.olap.type.MemberType::Unknown,
|
192
|
+
"Set" => Java::mondrian.olap.type.SetType.new(Java::mondrian.olap.type.MemberType::Unknown),
|
193
|
+
"Hierarchy" => Java::mondrian.olap.type.HierarchyType.new(nil, nil),
|
194
|
+
"Level" => Java::mondrian.olap.type.LevelType::Unknown
|
195
|
+
}
|
196
|
+
|
197
|
+
def getParameterTypes
|
198
|
+
@parameterTypes ||= self.class.parameters.map{|p| get_java_type(p)}
|
199
|
+
end
|
200
|
+
class_loader = JRuby.runtime.jruby_class_loader
|
201
|
+
type_array_class = java.lang.Class.forName "[Lmondrian.olap.type.Type;", true, class_loader
|
202
|
+
add_method_signature("getParameterTypes", [type_array_class])
|
203
|
+
|
204
|
+
def getReturnType(parameterTypes)
|
205
|
+
@returnType ||= get_java_type self.class.returns
|
206
|
+
end
|
207
|
+
add_method_signature("getReturnType", [Java::mondrian.olap.type.Type, type_array_class])
|
208
|
+
|
209
|
+
def getReservedWords
|
210
|
+
nil
|
211
|
+
end
|
212
|
+
string_array_class = java.lang.Class.forName "[Ljava.lang.String;", true, class_loader
|
213
|
+
add_method_signature("getReservedWords", [string_array_class])
|
214
|
+
|
215
|
+
def execute(evaluator, arguments)
|
216
|
+
values = []
|
217
|
+
self.class.parameters.each_with_index do |p,i|
|
218
|
+
value = UDF_SCALAR_TYPES[p] ? arguments[i].evaluateScalar(evaluator) : arguments[i].evaluate(evaluator)
|
219
|
+
values << value
|
220
|
+
end
|
221
|
+
call_with_evaluator(evaluator, *values)
|
222
|
+
end
|
223
|
+
arguments_array_class = java.lang.Class.forName "[Lmondrian.spi.UserDefinedFunction$Argument;", true, class_loader
|
224
|
+
add_method_signature("execute", [java.lang.Object, Java::mondrian.olap.Evaluator, arguments_array_class])
|
225
|
+
|
226
|
+
# Override this metho if evaluator is needed
|
227
|
+
def call_with_evaluator(evaluator, *values)
|
228
|
+
call(*values)
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
|
233
|
+
def get_java_type(type)
|
234
|
+
if type_class = UDF_SCALAR_TYPES[type]
|
235
|
+
type_class.new
|
236
|
+
else
|
237
|
+
UDF_OTHER_TYPES[type]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.stringified_type(type)
|
242
|
+
type = stringify(type)
|
243
|
+
raise ArgumentError, "invalid user defined function type #{type.inspect}" unless UDF_SCALAR_TYPES[type] || UDF_OTHER_TYPES[type]
|
244
|
+
type
|
245
|
+
end
|
246
|
+
|
247
|
+
def self.stringify(arg)
|
248
|
+
arg = arg.to_s.split('_').map{|s| s.capitalize}.join if arg.is_a? Symbol
|
249
|
+
arg
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def ruby(*options, &block)
|
254
|
+
udf_class_name = if options.include?(:shared)
|
255
|
+
"#{name.capitalize}Udf"
|
256
|
+
end
|
257
|
+
if udf_class_name && self.class.const_defined?(udf_class_name)
|
258
|
+
udf_class = self.class.const_get(udf_class_name)
|
259
|
+
else
|
260
|
+
udf_class = Class.new(RubyUdfBase)
|
261
|
+
self.class.const_set(udf_class_name, udf_class) if udf_class_name
|
262
|
+
end
|
263
|
+
udf_class.function_name = name
|
264
|
+
udf_class.class_eval(&block)
|
265
|
+
udf_java_class = udf_class.become_java!(false)
|
266
|
+
|
267
|
+
class_name udf_java_class.getName
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
class Script < SchemaElement
|
272
|
+
attributes :language
|
273
|
+
content :text
|
274
|
+
end
|
275
|
+
|
276
|
+
class CellFormatter < SchemaElement
|
277
|
+
include ScriptElements
|
278
|
+
# Name of a formatter class for the appropriate cell being displayed.
|
279
|
+
# The class must implement the mondrian.olap.CellFormatter interface.
|
280
|
+
attributes :class_name
|
281
|
+
elements :script
|
282
|
+
|
283
|
+
def initialize(name = nil, attributes = {}, parent = nil, &block)
|
284
|
+
super
|
285
|
+
if name && !attributes[:class_name] && !block_given?
|
286
|
+
# use shared ruby implementation
|
287
|
+
@attributes[:class_name] = ruby_formatter_java_class_name(name)
|
288
|
+
@attributes.delete(:name)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def coffeescript(text)
|
293
|
+
coffeescript_function('(value)', text)
|
294
|
+
end
|
295
|
+
|
296
|
+
def ruby(*options, &block)
|
297
|
+
ruby_formatter(options, Java::mondrian.spi.CellFormatter, 'formatCell', [java.lang.String, java.lang.Object], &block)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
class MemberFormatter < SchemaElement
|
302
|
+
include ScriptElements
|
303
|
+
attributes :class_name
|
304
|
+
elements :script
|
305
|
+
|
306
|
+
def coffeescript(text)
|
307
|
+
coffeescript_function('(member)', text)
|
308
|
+
end
|
309
|
+
|
310
|
+
def ruby(*options, &block)
|
311
|
+
ruby_formatter(options, Java::mondrian.spi.MemberFormatter, 'formatMember', [java.lang.String, Java::mondrian.olap.Member], &block)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
class PropertyFormatter < SchemaElement
|
316
|
+
include ScriptElements
|
317
|
+
attributes :class_name
|
318
|
+
elements :script
|
319
|
+
|
320
|
+
def coffeescript(text)
|
321
|
+
coffeescript_function('(member,propertyName,propertyValue)', text)
|
322
|
+
end
|
323
|
+
|
324
|
+
def ruby(*options, &block)
|
325
|
+
ruby_formatter(options, Java::mondrian.spi.PropertyFormatter, 'formatProperty', [java.lang.String, Java::mondrian.olap.Member, java.lang.String, java.lang.Object], &block)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Connection role" do
|
4
|
+
|
5
|
+
describe "create connection" do
|
6
|
+
before(:each) do
|
7
|
+
@role_name = role_name = 'California manager'
|
8
|
+
@role_name2 = role_name2 = 'Dummy, with comma'
|
9
|
+
@schema = Mondrian::OLAP::Schema.define do
|
10
|
+
cube 'Sales' do
|
11
|
+
table 'sales'
|
12
|
+
dimension 'Gender', :foreign_key => 'customer_id' do
|
13
|
+
hierarchy :has_all => true, :primary_key => 'id' do
|
14
|
+
table 'customers'
|
15
|
+
level 'Gender', :column => 'gender', :unique_members => true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
dimension 'Customers', :foreign_key => 'customer_id' do
|
19
|
+
hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
|
20
|
+
table 'customers'
|
21
|
+
level 'Country', :column => 'country', :unique_members => true
|
22
|
+
level 'State Province', :column => 'state_province', :unique_members => true
|
23
|
+
level 'City', :column => 'city', :unique_members => false
|
24
|
+
level 'Name', :column => 'fullname', :unique_members => true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
dimension 'Time', :foreign_key => 'time_id' do
|
28
|
+
hierarchy :has_all => false, :primary_key => 'id' do
|
29
|
+
table 'time'
|
30
|
+
level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true
|
31
|
+
level 'Quarter', :column => 'quarter', :unique_members => false
|
32
|
+
level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
measure 'Unit Sales', :column => 'unit_sales', :aggregator => 'sum'
|
36
|
+
measure 'Store Sales', :column => 'store_sales', :aggregator => 'sum'
|
37
|
+
end
|
38
|
+
role role_name do
|
39
|
+
schema_grant :access => 'none' do
|
40
|
+
cube_grant :cube => 'Sales', :access => 'all' do
|
41
|
+
dimension_grant :dimension => '[Measures]', :access => 'all'
|
42
|
+
hierarchy_grant :hierarchy => '[Customers]', :access => 'custom',
|
43
|
+
:top_level => '[Customers].[State Province]', :bottom_level => '[Customers].[City]' do
|
44
|
+
member_grant :member => '[Customers].[USA].[CA]', :access => 'all'
|
45
|
+
member_grant :member => '[Customers].[USA].[CA].[Los Angeles]', :access => 'none'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
role role_name2
|
51
|
+
|
52
|
+
# to test that Role elements are generated before UserDefinedFunction
|
53
|
+
user_defined_function 'Factorial' do
|
54
|
+
ruby do
|
55
|
+
parameters :numeric
|
56
|
+
returns :numeric
|
57
|
+
def call(n)
|
58
|
+
n <= 1 ? 1 : n * call(n - 1)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should connect" do
|
67
|
+
@olap.should be_connected
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should get available role names" do
|
71
|
+
@olap.available_role_names.should == [@role_name, @role_name2]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should not get role name if not set" do
|
75
|
+
@olap.role_name.should be_nil
|
76
|
+
@olap.role_names.should be_empty
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should set and get role name" do
|
80
|
+
@olap.role_name = @role_name
|
81
|
+
@olap.role_name.should == @role_name
|
82
|
+
@olap.role_names.should == [@role_name]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should raise error when invalid role name is set" do
|
86
|
+
expect {
|
87
|
+
@olap.role_name = 'invalid'
|
88
|
+
}.to raise_error {|e|
|
89
|
+
e.should be_kind_of(Mondrian::OLAP::Error)
|
90
|
+
e.message.should == "org.olap4j.OlapException: Unknown role 'invalid'"
|
91
|
+
e.root_cause_message.should == "Unknown role 'invalid'"
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should set and get several role names" do
|
96
|
+
@olap.role_names = [@role_name, @role_name2]
|
97
|
+
@olap.role_name.should == "[#{@role_name}, #{@role_name2}]"
|
98
|
+
@olap.role_names.should == [@role_name, @role_name2]
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should not get non-visible member" do
|
102
|
+
@cube = @olap.cube('Sales')
|
103
|
+
@cube.member('[Customers].[USA].[CA].[Los Angeles]').should_not be_nil
|
104
|
+
@olap.role_name = @role_name
|
105
|
+
@cube.member('[Customers].[USA].[CA].[Los Angeles]').should be_nil
|
106
|
+
end
|
107
|
+
|
108
|
+
# TODO: investigate why role name is not returned when set in connection string
|
109
|
+
# it "should set role name from connection parameters" do
|
110
|
+
# @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema,
|
111
|
+
# :role => @role_name)
|
112
|
+
# @olap.role_name.should == @role_name
|
113
|
+
# end
|
114
|
+
|
115
|
+
it "should not get non-visible member when role name set in connection parameters" do
|
116
|
+
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema,
|
117
|
+
:role => @role_name)
|
118
|
+
@cube = @olap.cube('Sales')
|
119
|
+
@cube.member('[Customers].[USA].[CA].[Los Angeles]').should be_nil
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should not get non-visible member when several role names set in connection parameters" do
|
123
|
+
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema,
|
124
|
+
:roles => [@role_name, @role_name2])
|
125
|
+
@cube = @olap.cube('Sales')
|
126
|
+
@cube.member('[Customers].[USA].[CA].[Los Angeles]').should be_nil
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|