mondrian 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format d
3
+ --profile
4
+ --backtrace
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mondrian.gemspec
4
+ gemspec
5
+
6
+
7
+ group :test, :development do
8
+ gem 'guard'
9
+ gem 'rspec'
10
+ gem 'pry'
11
+ gem 'equivalent-xml'
12
+ gem 'rb-fsevent'
13
+ gem 'mocha'
14
+ gem 'bourne'
15
+ gem 'guard-rspec'
16
+ gem 'linecache19', '>= 0.5.13'
17
+ gem 'ruby-debug-base19', '>= 0.11.26'
18
+ gem 'ruby-debug19', :require => 'ruby-debug'
19
+ end
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2, :all_on_start => true, :all_after_pass => true do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Scott Ellard, 2010-2011 Raimonds Simanovskis
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # Mondrian
2
+
3
+ A ruby DSL based off of https://github.com/rsim/mondrian-olap.
4
+
5
+ This gem only consists of the schema definition and not the olap server
6
+ itself. It is not dependent on jruby.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'mondrian'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install mondrian
21
+
22
+ ## Usage
23
+
24
+ ### Schema definition
25
+
26
+ At first you need to define OLAP schema mapping to relational database schema tables and columns. OLAP schema consists of:
27
+
28
+ * Cubes
29
+
30
+ Multidimensional cube is a collection of measures that can be accessed by dimensions. In relational database cubes are stored in fact tables with measure columns and dimension foreign key columns.
31
+
32
+ * Dimensions
33
+
34
+ Dimension can be used in one cube (private) or in many cubes (shared). In relational database dimensions are stored in dimension tables.
35
+
36
+ * Hierarchies and levels
37
+
38
+ Dimension has at least one primary hierarchy and optional additional hierarchies and each hierarchy has one or more levels. In relational database all levels can be stored in the same dimension table as different columns or can be stored also in several tables.
39
+
40
+ * Members
41
+
42
+ Dimension hierarchy level values are called members.
43
+
44
+ * Measures
45
+
46
+ Measures are values which can be accessed at detailed level or aggregated (e.g. as sum or average) at higher dimension hierarchy levels. In relational database measures are stored as columns in cube table.
47
+
48
+ * Calculated measures
49
+
50
+ Calculated measures are not stored in database but calculated using specified formula from other measures.
51
+
52
+ Read more about about [defining Mondrian OLAP schema](http://mondrian.pentaho.com/documentation/schema.php).
53
+
54
+ Here is example how to define OLAP schema and its mapping to relational database tables and columns using mondrian-olap:
55
+
56
+ require "rubygems"
57
+ require "mondrian"
58
+
59
+ schema = Mondrian::OLAP::Schema.define do
60
+ cube 'Sales' do
61
+ table 'sales'
62
+ dimension 'Customers', :foreign_key => 'customer_id' do
63
+ hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
64
+ table 'customers'
65
+ level 'Country', :column => 'country', :unique_members => true
66
+ level 'State Province', :column => 'state_province', :unique_members => true
67
+ level 'City', :column => 'city', :unique_members => false
68
+ level 'Name', :column => 'fullname', :unique_members => true
69
+ end
70
+ end
71
+ dimension 'Products', :foreign_key => 'product_id' do
72
+ hierarchy :has_all => true, :all_member_name => 'All Products',
73
+ :primary_key => 'id', :primary_key_table => 'products' do
74
+ join :left_key => 'product_class_id', :right_key => 'id' do
75
+ table 'products'
76
+ table 'product_classes'
77
+ end
78
+ level 'Product Family', :table => 'product_classes', :column => 'product_family', :unique_members => true
79
+ level 'Brand Name', :table => 'products', :column => 'brand_name', :unique_members => false
80
+ level 'Product Name', :table => 'products', :column => 'product_name', :unique_members => true
81
+ end
82
+ end
83
+ dimension 'Time', :foreign_key => 'time_id', :type => 'TimeDimension' do
84
+ hierarchy :has_all => false, :primary_key => 'id' do
85
+ table 'time'
86
+ level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true, :level_type => 'TimeYears'
87
+ level 'Quarter', :column => 'quarter', :unique_members => false, :level_type => 'TimeQuarters'
88
+ level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false, :level_type => 'TimeMonths'
89
+ end
90
+ hierarchy 'Weekly', :has_all => false, :primary_key => 'id' do
91
+ table 'time'
92
+ level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true, :level_type => 'TimeYears'
93
+ level 'Week', :column => 'weak_of_year', :type => 'Numeric', :unique_members => false, :level_type => 'TimeWeeks'
94
+ end
95
+ end
96
+ measure 'Unit Sales', :column => 'unit_sales', :aggregator => 'sum'
97
+ measure 'Store Sales', :column => 'store_sales', :aggregator => 'sum'
98
+ end
99
+ end
100
+
101
+ ## Contributing
102
+
103
+ 1. Fork it
104
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
105
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
106
+ 4. Push to the branch (`git push origin my-new-feature`)
107
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/lib/mondrian.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "mondrian/version"
2
+ require "mondrian/schema"
3
+
4
+ module Mondrian
5
+
6
+ end
@@ -0,0 +1,297 @@
1
+ require 'mondrian/schema_element'
2
+
3
+ module Mondrian
4
+ # See http://mondrian.pentaho.com/documentation/schema.php for more detailed description
5
+ # of Mondrian Schema elements.
6
+ class Schema < SchemaElement
7
+ def initialize(name = nil, attributes = {}, &block)
8
+ name, attributes = self.class.pre_process_arguments(name, attributes)
9
+ pre_process_attributes(attributes)
10
+ super(name, attributes, &block)
11
+ end
12
+
13
+ def self.define(name = nil, attributes = {}, &block)
14
+ name, attributes = pre_process_arguments(name, attributes)
15
+ new(name || 'default', attributes, &block)
16
+ end
17
+
18
+ def define(name = nil, attributes = {}, &block)
19
+ name, attributes = self.class.pre_process_arguments(name, attributes)
20
+ pre_process_attributes(attributes)
21
+ @attributes[:name] = name || @attributes[:name] || 'default' # otherwise connection with empty name fails
22
+ instance_eval &block if block
23
+ self
24
+ end
25
+
26
+ def include_schema(shared_schema)
27
+ shared_schema.class.elements.each do |element|
28
+ instance_variable_get("@#{pluralize(element)}").concat shared_schema.send(pluralize(element))
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def self.pre_process_arguments(name, attributes)
35
+ # if is called just with attributes hash and without name
36
+ if name.is_a?(Hash) && attributes.empty?
37
+ attributes = name
38
+ name = nil
39
+ end
40
+ [name, attributes]
41
+ end
42
+
43
+ def pre_process_attributes(attributes)
44
+ unless attributes[:upcase_data_dictionary].nil?
45
+ @upcase_data_dictionary = attributes.delete(:upcase_data_dictionary)
46
+ end
47
+ end
48
+
49
+ public
50
+
51
+ attributes :name, :description
52
+ elements :cube
53
+
54
+ class Cube < SchemaElement
55
+ attributes :name, :description,
56
+ # The name of the measure that would be taken as the default measure of the cube.
57
+ :default_measure,
58
+ # Should the Fact table data for this Cube be cached by Mondrian or not.
59
+ # The default action is to cache the data.
60
+ :cache,
61
+ # Whether element is enabled - if true, then the Cube is realized otherwise it is ignored.
62
+ :enabled
63
+ elements :table, :view, :dimension, :measure, :calculated_member
64
+ end
65
+
66
+ class Table < SchemaElement
67
+ attributes :name, :schema, # Optional qualifier for table.
68
+ # Alias to be used with this table when it is used to form queries.
69
+ # If not specified, defaults to the table name, but in any case, must be unique within the schema.
70
+ # (You can use the same table in different hierarchies, but it must have different aliases.)
71
+ :alias
72
+ data_dictionary_names :name, :schema, :alias # values in XML will be uppercased when using Oracle driver
73
+ elements :agg_exclude, :agg_name, :agg_pattern, :sql
74
+ end
75
+
76
+ class View < SchemaElement
77
+ attributes :alias
78
+ data_dictionary_names :alias
79
+ # Defines a "table" using SQL query which can have different variants for different underlying databases
80
+ elements :sql
81
+ end
82
+
83
+ class Dimension < SchemaElement
84
+ attributes :name, :description,
85
+ # The dimension's type may be one of "Standard" or "Time".
86
+ # A time dimension will allow the use of the MDX time functions (WTD, YTD, QTD, etc.).
87
+ # Use a standard dimension if the dimension is not a time-related dimension.
88
+ # The default value is "Standard".
89
+ :type,
90
+ # The name of the column in the fact table which joins to the leaf level of this dimension.
91
+ # Required in a private Dimension or a DimensionUsage, but not in a public Dimension.
92
+ :foreign_key
93
+ data_dictionary_names :foreign_key # values in XML will be uppercased when using Oracle driver
94
+ elements :hierarchy
95
+ end
96
+
97
+ class Hierarchy < SchemaElement
98
+ attributes :name, :description,
99
+ # Whether this hierarchy has an 'all' member.
100
+ :has_all,
101
+ # Name of the 'all' member. If this attribute is not specified,
102
+ # the all member is named 'All hierarchyName', for example, 'All Store'.
103
+ :all_member_name,
104
+ # Name of the 'all' level. If this attribute is not specified,
105
+ # the all member is named '(All)'.
106
+ :all_level_name,
107
+ # The name of the column which identifies members, and which is referenced by rows in the fact table.
108
+ # If not specified, the key of the lowest level is used. See also Dimension foreign_key.
109
+ :primary_key,
110
+ # The name of the table which contains primary_key.
111
+ # If the hierarchy has only one table, defaults to that; it is required.
112
+ :primary_key_table,
113
+ # Should be set to the level (if such a level exists) at which depth it is known
114
+ # that all members have entirely unique rows, allowing SQL GROUP BY clauses to be completely eliminated from the query.
115
+ :unique_key_level_name
116
+ data_dictionary_names :primary_key, :primary_key_table # values in XML will be uppercased when using Oracle driver
117
+ elements :table, :join, :property, :level
118
+ end
119
+
120
+ class Join < SchemaElement
121
+ attributes :left_key, :right_key, :left_alias, :right_alias
122
+ data_dictionary_names :left_key, :right_key, :left_alias, :right_alias # values in XML will be uppercased when using Oracle driver
123
+ elements :table, :join
124
+ end
125
+
126
+ class Level < SchemaElement
127
+ attributes :name, :description,
128
+ # The name of the table that the column comes from.
129
+ # If this hierarchy is based upon just one table, defaults to the name of that table;
130
+ # otherwise, it is required.
131
+ :table,
132
+ # The name of the column which holds the unique identifier of this level.
133
+ :column,
134
+ # The name of the column which holds the user identifier of this level.
135
+ :name_column,
136
+ # The name of the column which holds member ordinals.
137
+ # If this column is not specified, the key column is used for ordering.
138
+ :ordinal_column,
139
+ # The name of the column which references the parent member in a parent-child hierarchy.
140
+ :parent_column,
141
+ # Value which identifies null parents in a parent-child hierarchy.
142
+ # Typical values are 'NULL' and '0'.
143
+ :null_parent_value,
144
+ # Indicates the type of this level's key column:
145
+ # String, Numeric, Integer, Boolean, Date, Time or Timestamp.
146
+ # When generating SQL statements, Mondrian encloses values for String columns in quotation marks,
147
+ # but leaves values for Integer and Numeric columns un-quoted.
148
+ # Date, Time, and Timestamp values are quoted according to the SQL dialect.
149
+ # For a SQL-compliant dialect, the values appear prefixed by their typename,
150
+ # for example, "DATE '2006-06-01'".
151
+ # Default value: 'String'
152
+ :type,
153
+ # Whether members are unique across all parents.
154
+ # For example, zipcodes are unique across all states.
155
+ # The first level's members are always unique.
156
+ # Default value: false
157
+ :unique_members,
158
+ # Whether this is a regular or a time-related level.
159
+ # The value makes a difference to time-related functions such as YTD (year-to-date).
160
+ # Default value: 'Regular'
161
+ :level_type,
162
+ # Condition which determines whether a member of this level is hidden.
163
+ # If a hierarchy has one or more levels with hidden members,
164
+ # then it is possible that not all leaf members are the same distance from the root,
165
+ # and it is termed a ragged hierarchy.
166
+ # Allowable values are: Never (a member always appears; the default);
167
+ # IfBlankName (a member doesn't appear if its name is null, empty or all whitespace);
168
+ # and IfParentsName (a member appears unless its name matches the parent's.
169
+ # Default value: 'Never'
170
+ :hide_member_if
171
+ data_dictionary_names :table, :column, :name_column, :ordinal_column, :parent_column # values in XML will be uppercased when using Oracle driver
172
+ elements :key_expression, :name_expression, :ordinal_expression, :member_formatter, :property
173
+ end
174
+
175
+ class KeyExpression < SchemaElement
176
+ elements :sql
177
+ end
178
+
179
+ class NameExpression < SchemaElement
180
+ elements :sql
181
+ end
182
+
183
+ class OrdinalExpression < SchemaElement
184
+ elements :sql
185
+ end
186
+
187
+ class Sql < SchemaElement
188
+ def self.name
189
+ 'SQL'
190
+ end
191
+ attributes :dialect
192
+ content :text
193
+ end
194
+
195
+ class Property < SchemaElement
196
+ attributes :name, :description,
197
+ :column,
198
+ # Data type of this property: String, Numeric, Integer, Boolean, Date, Time or Timestamp.
199
+ :type,
200
+ # Should be set to true if the value of the property is functionally dependent on the level value.
201
+ # This permits the associated property column to be omitted from the GROUP BY clause
202
+ # (if the database permits columns in the SELECT that are not in the GROUP BY).
203
+ # This can be a significant performance enhancement on some databases, such as MySQL.
204
+ :depends_on_level_value
205
+ elements :property_formatter
206
+ end
207
+
208
+ class Measure < SchemaElement
209
+ attributes :name, :description,
210
+ # Column which is source of this measure's values.
211
+ # If not specified, a measure expression must be specified.
212
+ :column,
213
+ # The datatype of this measure: String, Numeric, Integer, Boolean, Date, Time or Timestamp.
214
+ # The default datatype of a measure is 'Integer' if the measure's aggregator is 'Count', otherwise it is 'Numeric'.
215
+ :datatype,
216
+ # Aggregation function. Allowed values are "sum", "count", "min", "max", "avg", and "distinct-count".
217
+ :aggregator,
218
+ # Format string with which to format cells of this measure. For more details, see the mondrian.util.Format class.
219
+ :format_string,
220
+ # Whether this member is visible in the user-interface. Default true.
221
+ :visible
222
+ data_dictionary_names :column # values in XML will be uppercased when using Oracle driver
223
+ elements :measure_expression, :cell_formatter
224
+ end
225
+
226
+ class MeasureExpression < SchemaElement
227
+ elements :sql
228
+ end
229
+
230
+ class CalculatedMember < SchemaElement
231
+ attributes :name, :description,
232
+ # Name of the dimension which this member belongs to.
233
+ :dimension,
234
+ # Format string with which to format cells of this measure. For more details, see the mondrian.util.Format class.
235
+ :format_string,
236
+ # Whether this member is visible in the user-interface. Default true.
237
+ :visible
238
+ elements :formula, :calculated_member_property, :cell_formatter
239
+ end
240
+
241
+ class Formula < SchemaElement
242
+ content :text
243
+ end
244
+
245
+ class CalculatedMemberProperty < SchemaElement
246
+ attributes :name, :description,
247
+ # MDX expression which defines the value of this property. If the expression is a constant string, you could enclose it in quotes,
248
+ # or just specify the 'value' attribute instead.
249
+ :expression,
250
+ # Value of this property. If the value is not constant, specify the 'expression' attribute instead.
251
+ :value
252
+ end
253
+
254
+ class AggName < SchemaElement
255
+ attributes :name
256
+ data_dictionary_names :name
257
+ elements :agg_fact_count, :agg_measure, :agg_level, :agg_foreign_key
258
+ end
259
+
260
+ class AggFactCount < SchemaElement
261
+ attributes :column
262
+ data_dictionary_names :column
263
+ end
264
+
265
+ class AggMeasure < SchemaElement
266
+ attributes :name, :column
267
+ data_dictionary_names :column
268
+ end
269
+
270
+ class AggLevel < SchemaElement
271
+ attributes :name, :column
272
+ data_dictionary_names :column
273
+ end
274
+
275
+ class AggForeignKey < SchemaElement
276
+ attributes :fact_column, :agg_column
277
+ data_dictionary_names :fact_column, :agg_column
278
+ end
279
+
280
+ class AggIgnoreColumn < SchemaElement
281
+ attributes :column
282
+ data_dictionary_names :column
283
+ end
284
+
285
+ class AggPattern < SchemaElement
286
+ attributes :pattern
287
+ data_dictionary_names :pattern
288
+ elements :agg_fact_count, :agg_measure, :agg_level, :agg_foreign_key, :agg_exclude
289
+ end
290
+
291
+ class AggExclude < SchemaElement
292
+ attributes :name, :pattern, :ignorecase
293
+ data_dictionary_names :name, :pattern
294
+ end
295
+
296
+ end
297
+ end