mondrian-olap 0.1.0

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