mondrian-olap 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.rspec +2 -0
  2. data/Gemfile +15 -0
  3. data/LICENSE-Mondrian.html +259 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.rdoc +219 -0
  6. data/RUNNING_TESTS.rdoc +41 -0
  7. data/Rakefile +46 -0
  8. data/VERSION +1 -0
  9. data/lib/mondrian-olap.rb +1 -0
  10. data/lib/mondrian/jars/commons-collections-3.1.jar +0 -0
  11. data/lib/mondrian/jars/commons-dbcp-1.2.1.jar +0 -0
  12. data/lib/mondrian/jars/commons-logging-1.0.4.jar +0 -0
  13. data/lib/mondrian/jars/commons-math-1.0.jar +0 -0
  14. data/lib/mondrian/jars/commons-pool-1.2.jar +0 -0
  15. data/lib/mondrian/jars/commons-vfs-1.0.jar +0 -0
  16. data/lib/mondrian/jars/eigenbase-properties.jar +0 -0
  17. data/lib/mondrian/jars/eigenbase-resgen.jar +0 -0
  18. data/lib/mondrian/jars/eigenbase-xom.jar +0 -0
  19. data/lib/mondrian/jars/javacup.jar +0 -0
  20. data/lib/mondrian/jars/log4j-1.2.8.jar +0 -0
  21. data/lib/mondrian/jars/log4j.properties +18 -0
  22. data/lib/mondrian/jars/mondrian.jar +0 -0
  23. data/lib/mondrian/jars/olap4j.jar +0 -0
  24. data/lib/mondrian/olap.rb +14 -0
  25. data/lib/mondrian/olap/connection.rb +122 -0
  26. data/lib/mondrian/olap/cube.rb +236 -0
  27. data/lib/mondrian/olap/query.rb +313 -0
  28. data/lib/mondrian/olap/result.rb +155 -0
  29. data/lib/mondrian/olap/schema.rb +158 -0
  30. data/lib/mondrian/olap/schema_element.rb +123 -0
  31. data/mondrian-olap.gemspec +116 -0
  32. data/spec/connection_spec.rb +56 -0
  33. data/spec/cube_spec.rb +259 -0
  34. data/spec/fixtures/MondrianTest.xml +128 -0
  35. data/spec/fixtures/MondrianTestOracle.xml +128 -0
  36. data/spec/query_spec.rb +582 -0
  37. data/spec/rake_tasks.rb +185 -0
  38. data/spec/schema_definition_spec.rb +345 -0
  39. data/spec/spec_helper.rb +67 -0
  40. data/spec/support/matchers/be_like.rb +24 -0
  41. metadata +217 -0
@@ -0,0 +1,158 @@
1
+ require 'mondrian/olap/schema_element'
2
+
3
+ module Mondrian
4
+ module OLAP
5
+ # See http://mondrian.pentaho.com/documentation/schema.php for more detailed description
6
+ # of Mondrian Schema elements.
7
+ class Schema < SchemaElement
8
+ def self.define(name = nil, attributes = {}, &block)
9
+ new(name || 'default', attributes, &block)
10
+ end
11
+
12
+ def define(name = nil, &block)
13
+ @attributes[:name] = name || 'default' # otherwise connection with empty name fails
14
+ instance_eval &block if block
15
+ self
16
+ end
17
+
18
+ attributes :description
19
+ elements :cube
20
+
21
+ class Cube < SchemaElement
22
+ attributes :description,
23
+ # The name of the measure that would be taken as the default measure of the cube.
24
+ :default_measure,
25
+ # Should the Fact table data for this Cube be cached by Mondrian or not.
26
+ # The default action is to cache the data.
27
+ :cache,
28
+ # Whether element is enabled - if true, then the Cube is realized otherwise it is ignored.
29
+ :enabled
30
+ elements :table, :dimension, :measure, :calculated_member
31
+ end
32
+
33
+ class Table < SchemaElement
34
+ attributes :schema, # Optional qualifier for table.
35
+ # Alias to be used with this table when it is used to form queries.
36
+ # If not specified, defaults to the table name, but in any case, must be unique within the schema.
37
+ # (You can use the same table in different hierarchies, but it must have different aliases.)
38
+ :alias
39
+ data_dictionary_names :name, :schema, :alias # values in XML will be uppercased when using Oracle driver
40
+ end
41
+
42
+ class Dimension < SchemaElement
43
+ attributes :description,
44
+ # The dimension's type may be one of "Standard" or "Time".
45
+ # A time dimension will allow the use of the MDX time functions (WTD, YTD, QTD, etc.).
46
+ # Use a standard dimension if the dimension is not a time-related dimension.
47
+ # The default value is "Standard".
48
+ :type,
49
+ # The name of the column in the fact table which joins to the leaf level of this dimension.
50
+ # Required in a private Dimension or a DimensionUsage, but not in a public Dimension.
51
+ :foreign_key
52
+ data_dictionary_names :foreign_key # values in XML will be uppercased when using Oracle driver
53
+ elements :hierarchy
54
+ end
55
+
56
+ class Hierarchy < SchemaElement
57
+ attributes :description,
58
+ # Whether this hierarchy has an 'all' member.
59
+ :has_all,
60
+ # Name of the 'all' member. If this attribute is not specified,
61
+ # the all member is named 'All hierarchyName', for example, 'All Store'.
62
+ :all_member_name,
63
+ # Name of the 'all' level. If this attribute is not specified,
64
+ # the all member is named '(All)'.
65
+ :all_level_name,
66
+ # The name of the column which identifies members, and which is referenced by rows in the fact table.
67
+ # If not specified, the key of the lowest level is used. See also Dimension foreign_key.
68
+ :primary_key,
69
+ # The name of the table which contains primary_key.
70
+ # If the hierarchy has only one table, defaults to that; it is required.
71
+ :primary_key_table,
72
+ # Should be set to the level (if such a level exists) at which depth it is known
73
+ # that all members have entirely unique rows, allowing SQL GROUP BY clauses to be completely eliminated from the query.
74
+ :unique_key_level_name
75
+ data_dictionary_names :primary_key, :primary_key_table # values in XML will be uppercased when using Oracle driver
76
+ elements :table, :join, :level
77
+ end
78
+
79
+ class Join < SchemaElement
80
+ attributes :left_key, :right_key
81
+ data_dictionary_names :left_key, :right_key # values in XML will be uppercased when using Oracle driver
82
+ elements :table
83
+ end
84
+
85
+ class Level < SchemaElement
86
+ attributes :description,
87
+ # The name of the table that the column comes from.
88
+ # If this hierarchy is based upon just one table, defaults to the name of that table;
89
+ # otherwise, it is required.
90
+ :table,
91
+ # The name of the column which holds the unique identifier of this level.
92
+ :column,
93
+ # The name of the column which holds the user identifier of this level.
94
+ :name_column,
95
+ # The name of the column which holds member ordinals.
96
+ # If this column is not specified, the key column is used for ordering.
97
+ :ordinal_column,
98
+ # The name of the column which references the parent member in a parent-child hierarchy.
99
+ :parent_column,
100
+ # Value which identifies null parents in a parent-child hierarchy.
101
+ # Typical values are 'NULL' and '0'.
102
+ :null_parent_value,
103
+ # Indicates the type of this level's key column:
104
+ # String, Numeric, Integer, Boolean, Date, Time or Timestamp.
105
+ # When generating SQL statements, Mondrian encloses values for String columns in quotation marks,
106
+ # but leaves values for Integer and Numeric columns un-quoted.
107
+ # Date, Time, and Timestamp values are quoted according to the SQL dialect.
108
+ # For a SQL-compliant dialect, the values appear prefixed by their typename,
109
+ # for example, "DATE '2006-06-01'".
110
+ # Default value: 'String'
111
+ :type,
112
+ # Whether members are unique across all parents.
113
+ # For example, zipcodes are unique across all states.
114
+ # The first level's members are always unique.
115
+ # Default value: false
116
+ :unique_members,
117
+ # Whether this is a regular or a time-related level.
118
+ # The value makes a difference to time-related functions such as YTD (year-to-date).
119
+ # Default value: 'Regular'
120
+ :level_type,
121
+ # Condition which determines whether a member of this level is hidden.
122
+ # If a hierarchy has one or more levels with hidden members,
123
+ # then it is possible that not all leaf members are the same distance from the root,
124
+ # and it is termed a ragged hierarchy.
125
+ # Allowable values are: Never (a member always appears; the default);
126
+ # IfBlankName (a member doesn't appear if its name is null, empty or all whitespace);
127
+ # and IfParentsName (a member appears unless its name matches the parent's.
128
+ # Default value: 'Never'
129
+ :hide_member_if
130
+ data_dictionary_names :table, :column, :name_column, :ordinal_column, :parent_column # values in XML will be uppercased when using Oracle driver
131
+ end
132
+
133
+ class Measure < SchemaElement
134
+ attributes :description,
135
+ # Column which is source of this measure's values.
136
+ # If not specified, a measure expression must be specified.
137
+ :column,
138
+ # The datatype of this measure: String, Numeric, Integer, Boolean, Date, Time or Timestamp.
139
+ # The default datatype of a measure is 'Integer' if the measure's aggregator is 'Count', otherwise it is 'Numeric'.
140
+ :datatype,
141
+ # Aggregation function. Allowed values are "sum", "count", "min", "max", "avg", and "distinct-count".
142
+ :aggregator,
143
+ # Format string with which to format cells of this measure. For more details, see the mondrian.util.Format class.
144
+ :format_string
145
+ data_dictionary_names :column # values in XML will be uppercased when using Oracle driver
146
+ end
147
+
148
+ class CalculatedMember < SchemaElement
149
+ attributes :description,
150
+ # MDX expression which gives the value of this member. Equivalent to the Formula sub-element.
151
+ :formula,
152
+ # Name of the dimension which this member belongs to.
153
+ :dimension
154
+ end
155
+
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,123 @@
1
+ require 'nokogiri'
2
+
3
+ module Mondrian
4
+ module OLAP
5
+ class SchemaElement
6
+ def initialize(name = nil, attributes = {}, &block)
7
+ # if just attributes hash provided
8
+ if name.is_a?(Hash) && attributes == {}
9
+ attributes = name
10
+ name = nil
11
+ end
12
+ @attributes = {}
13
+ @attributes[:name] = name if name
14
+ @attributes.merge!(attributes)
15
+ self.class.elements.each do |element|
16
+ instance_variable_set("@#{pluralize(element)}", [])
17
+ end
18
+ instance_eval &block if block
19
+ end
20
+
21
+ def self.attributes(*names)
22
+ names.each do |name|
23
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
24
+ def #{name}(*args)
25
+ if args.empty?
26
+ @attributes[:#{name}]
27
+ elsif args.size == 1
28
+ @attributes[:#{name}] = args[0]
29
+ else
30
+ raise ArgumentError, "too many arguments"
31
+ end
32
+ end
33
+ RUBY
34
+ end
35
+ end
36
+
37
+ def self.data_dictionary_names(*names)
38
+ return @data_dictionary_names || [] if names.empty?
39
+ @data_dictionary_names ||= []
40
+ @data_dictionary_names.concat(names)
41
+ end
42
+
43
+ def self.elements(*names)
44
+ return @elements || [] if names.empty?
45
+
46
+ @elements ||= []
47
+ @elements.concat(names)
48
+
49
+ names.each do |name|
50
+ attr_reader pluralize(name).to_sym
51
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
52
+ def #{name}(name=nil, attributes = {}, &block)
53
+ @#{pluralize(name)} << Schema::#{camel_case(name)}.new(name, attributes, &block)
54
+ end
55
+ RUBY
56
+ end
57
+ end
58
+
59
+ def to_xml(options={})
60
+ Nokogiri::XML::Builder.new do |xml|
61
+ add_to_xml(xml, options)
62
+ end.to_xml
63
+ end
64
+
65
+ protected
66
+
67
+ def add_to_xml(xml, options)
68
+ xml.send(tag_name(self.class.name), xmlized_attributes(options)) do
69
+ self.class.elements.each do |element|
70
+ instance_variable_get("@#{pluralize(element)}").each {|item| item.add_to_xml(xml, options)}
71
+ end
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def xmlized_attributes(options)
78
+ # data dictionary values should be in uppercase when using Oracle driver
79
+ upcase_attributes = if options[:driver] == 'oracle'
80
+ self.class.data_dictionary_names
81
+ else
82
+ []
83
+ end
84
+ hash = {}
85
+ @attributes.each do |attr, value|
86
+ value = value.upcase if upcase_attributes.include?(attr)
87
+ hash[
88
+ # camelcase attribute name
89
+ attr.to_s.gsub(/_([^_]+)/){|m| $1.capitalize}
90
+ ] = value
91
+ end
92
+ hash
93
+ end
94
+
95
+ def self.pluralize(string)
96
+ string = string.to_s
97
+ case string
98
+ when /^(.*)y$/
99
+ "#{$1}ies"
100
+ else
101
+ "#{string}s"
102
+ end
103
+ end
104
+
105
+ def pluralize(string)
106
+ self.class.pluralize(string)
107
+ end
108
+
109
+ def self.camel_case(string)
110
+ string.to_s.split('_').map{|s| s.capitalize}.join('')
111
+ end
112
+
113
+ def camel_case(string)
114
+ self.class.camel_case(string)
115
+ end
116
+
117
+ def tag_name(string)
118
+ string.split('::').last << '_'
119
+ end
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,116 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mondrian-olap}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Raimonds Simanovskis"]
12
+ s.date = %q{2011-03-19}
13
+ s.description = %q{JRuby gem for performing multidimensional queries of relational database data using Mondrian OLAP Java library
14
+ }
15
+ s.email = %q{raimonds.simanovskis@gmail.com}
16
+ s.extra_rdoc_files = [
17
+ "LICENSE-Mondrian.html",
18
+ "LICENSE.txt",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".rspec",
23
+ "Gemfile",
24
+ "LICENSE-Mondrian.html",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "RUNNING_TESTS.rdoc",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "lib/mondrian-olap.rb",
31
+ "lib/mondrian/jars/commons-collections-3.1.jar",
32
+ "lib/mondrian/jars/commons-dbcp-1.2.1.jar",
33
+ "lib/mondrian/jars/commons-logging-1.0.4.jar",
34
+ "lib/mondrian/jars/commons-math-1.0.jar",
35
+ "lib/mondrian/jars/commons-pool-1.2.jar",
36
+ "lib/mondrian/jars/commons-vfs-1.0.jar",
37
+ "lib/mondrian/jars/eigenbase-properties.jar",
38
+ "lib/mondrian/jars/eigenbase-resgen.jar",
39
+ "lib/mondrian/jars/eigenbase-xom.jar",
40
+ "lib/mondrian/jars/javacup.jar",
41
+ "lib/mondrian/jars/log4j-1.2.8.jar",
42
+ "lib/mondrian/jars/log4j.properties",
43
+ "lib/mondrian/jars/mondrian.jar",
44
+ "lib/mondrian/jars/olap4j.jar",
45
+ "lib/mondrian/olap.rb",
46
+ "lib/mondrian/olap/connection.rb",
47
+ "lib/mondrian/olap/cube.rb",
48
+ "lib/mondrian/olap/query.rb",
49
+ "lib/mondrian/olap/result.rb",
50
+ "lib/mondrian/olap/schema.rb",
51
+ "lib/mondrian/olap/schema_element.rb",
52
+ "mondrian-olap.gemspec",
53
+ "spec/connection_spec.rb",
54
+ "spec/cube_spec.rb",
55
+ "spec/fixtures/MondrianTest.xml",
56
+ "spec/fixtures/MondrianTestOracle.xml",
57
+ "spec/query_spec.rb",
58
+ "spec/rake_tasks.rb",
59
+ "spec/schema_definition_spec.rb",
60
+ "spec/spec_helper.rb",
61
+ "spec/support/matchers/be_like.rb"
62
+ ]
63
+ s.homepage = %q{http://github.com/rsim/mondrian-olap}
64
+ s.require_paths = ["lib"]
65
+ s.rubygems_version = %q{1.5.1}
66
+ s.summary = %q{JRuby API for Mondrian OLAP Java library}
67
+ s.test_files = [
68
+ "spec/connection_spec.rb",
69
+ "spec/cube_spec.rb",
70
+ "spec/query_spec.rb",
71
+ "spec/rake_tasks.rb",
72
+ "spec/schema_definition_spec.rb",
73
+ "spec/spec_helper.rb",
74
+ "spec/support/matchers/be_like.rb"
75
+ ]
76
+
77
+ if s.respond_to? :specification_version then
78
+ s.specification_version = 3
79
+
80
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
81
+ s.add_runtime_dependency(%q<nokogiri>, ["~> 1.5.0.beta.4"])
82
+ s.add_development_dependency(%q<jruby-openssl>, [">= 0"])
83
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
84
+ s.add_development_dependency(%q<rspec>, ["~> 2.5"])
85
+ s.add_development_dependency(%q<autotest>, [">= 0"])
86
+ s.add_development_dependency(%q<jdbc-mysql>, [">= 0"])
87
+ s.add_development_dependency(%q<jdbc-postgres>, [">= 0"])
88
+ s.add_development_dependency(%q<activerecord>, ["~> 3.0.5"])
89
+ s.add_development_dependency(%q<activerecord-jdbc-adapter>, [">= 0"])
90
+ s.add_development_dependency(%q<activerecord-oracle_enhanced-adapter>, [">= 0"])
91
+ else
92
+ s.add_dependency(%q<nokogiri>, ["~> 1.5.0.beta.4"])
93
+ s.add_dependency(%q<jruby-openssl>, [">= 0"])
94
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
95
+ s.add_dependency(%q<rspec>, ["~> 2.5"])
96
+ s.add_dependency(%q<autotest>, [">= 0"])
97
+ s.add_dependency(%q<jdbc-mysql>, [">= 0"])
98
+ s.add_dependency(%q<jdbc-postgres>, [">= 0"])
99
+ s.add_dependency(%q<activerecord>, ["~> 3.0.5"])
100
+ s.add_dependency(%q<activerecord-jdbc-adapter>, [">= 0"])
101
+ s.add_dependency(%q<activerecord-oracle_enhanced-adapter>, [">= 0"])
102
+ end
103
+ else
104
+ s.add_dependency(%q<nokogiri>, ["~> 1.5.0.beta.4"])
105
+ s.add_dependency(%q<jruby-openssl>, [">= 0"])
106
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
107
+ s.add_dependency(%q<rspec>, ["~> 2.5"])
108
+ s.add_dependency(%q<autotest>, [">= 0"])
109
+ s.add_dependency(%q<jdbc-mysql>, [">= 0"])
110
+ s.add_dependency(%q<jdbc-postgres>, [">= 0"])
111
+ s.add_dependency(%q<activerecord>, ["~> 3.0.5"])
112
+ s.add_dependency(%q<activerecord-jdbc-adapter>, [">= 0"])
113
+ s.add_dependency(%q<activerecord-oracle_enhanced-adapter>, [">= 0"])
114
+ end
115
+ end
116
+
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+
3
+ describe "Connection" do
4
+
5
+ describe "create" do
6
+ before(:each) do
7
+ @olap = Mondrian::OLAP::Connection.new(CONNECTION_PARAMS_WITH_CATALOG)
8
+ end
9
+
10
+ it "should not be connected before connection" do
11
+ @olap.should_not be_connected
12
+ end
13
+
14
+ it "should be successful" do
15
+ @olap.connect.should be_true
16
+ end
17
+
18
+ end
19
+
20
+ describe "create with catalog content" do
21
+ before(:all) do
22
+ @schema_xml = File.read(CATALOG_FILE)
23
+ end
24
+ it "should be successful" do
25
+ @olap = Mondrian::OLAP::Connection.new(CONNECTION_PARAMS.merge(
26
+ :catalog_content => @schema_xml
27
+ ))
28
+ @olap.connect.should be_true
29
+ end
30
+
31
+ end
32
+
33
+ describe "properties" do
34
+ before(:all) do
35
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS_WITH_CATALOG)
36
+ end
37
+
38
+ it "should be connected" do
39
+ @olap.should be_connected
40
+ end
41
+
42
+ end
43
+
44
+ describe "close" do
45
+ before(:all) do
46
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS_WITH_CATALOG)
47
+ end
48
+
49
+ it "should not be connected after close" do
50
+ @olap.close
51
+ @olap.should_not be_connected
52
+ end
53
+
54
+ end
55
+
56
+ end