Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

...
  • 14 commits
  • 19 files changed
  • 0 commit comments
  • 3 contributors
18 Changelog.md
View
@@ -1,3 +1,21 @@
+### 0.4.0 / 2012-12-03
+
+* New features
+ * upgraded to latest Mondrian 3.5 version (build from 2012-11-29)
+ as well as corresponding olap4j 1.0.1 version
+ * support for JRuby 1.7 and Java 7 VM
+ * user defined functions and formatters in JavaScript, CoffeeScript and Ruby
+ * shared user defined functions in Ruby
+ * all exceptions are wrapped in Mondrian::OLAP::Error exception with root_cause_message method
+ * drill through from result cell to source measure and dimension table rows
+ * support for Mondrian schema roles to limit cube data access
+* Improvements
+ * get description of cube, dimension, hierarchy and level from schema definition
+ * visible? method for measures and calculated members
+ * nonempty_crossjoin query builder method
+ * schema definition with nested table joins
+ * added approx_row_count schema level attribute
+
### 0.3.0 / 2011-11-12
* New features
5 Gemfile
View
@@ -12,9 +12,10 @@ group :development do
gem 'jdbc-postgres'
gem 'jdbc-luciddb'
gem 'jdbc-jtds'
- gem 'activerecord', '= 3.0.10'
- gem 'activerecord-jdbc-adapter', '= 1.1.1'
+ gem 'activerecord', '= 3.2.8'
+ gem 'activerecord-jdbc-adapter', '= 1.2.2'
gem 'activerecord-oracle_enhanced-adapter', :require => false
gem 'coffee-script', '~> 2.2.0'
gem 'therubyrhino', '~> 1.73.1'
+ gem 'pry', :require => false
end
2  LICENSE.txt
View
@@ -1,6 +1,6 @@
(The MIT License)
-Copyright (c) 2010-2011 Raimonds Simanovskis
+Copyright (c) 2010-2012 Raimonds Simanovskis
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
265 README.md
View
@@ -49,77 +49,83 @@ Read more about about [defining Mondrian OLAP schema](http://mondrian.pentaho.co
Here is example how to define OLAP schema and its mapping to relational database tables and columns using mondrian-olap:
- require "rubygems"
- require "mondrian-olap"
-
- schema = Mondrian::OLAP::Schema.define do
- cube 'Sales' do
- table 'sales'
- dimension 'Customers', :foreign_key => 'customer_id' do
- hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
- table 'customers'
- level 'Country', :column => 'country', :unique_members => true
- level 'State Province', :column => 'state_province', :unique_members => true
- level 'City', :column => 'city', :unique_members => false
- level 'Name', :column => 'fullname', :unique_members => true
- end
- end
- dimension 'Products', :foreign_key => 'product_id' do
- hierarchy :has_all => true, :all_member_name => 'All Products',
- :primary_key => 'id', :primary_key_table => 'products' do
- join :left_key => 'product_class_id', :right_key => 'id' do
- table 'products'
- table 'product_classes'
- end
- level 'Product Family', :table => 'product_classes', :column => 'product_family', :unique_members => true
- level 'Brand Name', :table => 'products', :column => 'brand_name', :unique_members => false
- level 'Product Name', :table => 'products', :column => 'product_name', :unique_members => true
- end
- end
- dimension 'Time', :foreign_key => 'time_id', :type => 'TimeDimension' do
- hierarchy :has_all => false, :primary_key => 'id' do
- table 'time'
- level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true, :level_type => 'TimeYears'
- level 'Quarter', :column => 'quarter', :unique_members => false, :level_type => 'TimeQuarters'
- level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false, :level_type => 'TimeMonths'
- end
- hierarchy 'Weekly', :has_all => false, :primary_key => 'id' do
- table 'time'
- level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true, :level_type => 'TimeYears'
- level 'Week', :column => 'weak_of_year', :type => 'Numeric', :unique_members => false, :level_type => 'TimeWeeks'
- end
+```ruby
+require "rubygems"
+require "mondrian-olap"
+
+schema = Mondrian::OLAP::Schema.define do
+ cube 'Sales' do
+ table 'sales'
+ dimension 'Customers', :foreign_key => 'customer_id' do
+ hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
+ table 'customers'
+ level 'Country', :column => 'country', :unique_members => true
+ level 'State Province', :column => 'state_province', :unique_members => true
+ level 'City', :column => 'city', :unique_members => false
+ level 'Name', :column => 'fullname', :unique_members => true
+ end
+ end
+ dimension 'Products', :foreign_key => 'product_id' do
+ hierarchy :has_all => true, :all_member_name => 'All Products',
+ :primary_key => 'id', :primary_key_table => 'products' do
+ join :left_key => 'product_class_id', :right_key => 'id' do
+ table 'products'
+ table 'product_classes'
end
- measure 'Unit Sales', :column => 'unit_sales', :aggregator => 'sum'
- measure 'Store Sales', :column => 'store_sales', :aggregator => 'sum'
+ level 'Product Family', :table => 'product_classes', :column => 'product_family', :unique_members => true
+ level 'Brand Name', :table => 'products', :column => 'brand_name', :unique_members => false
+ level 'Product Name', :table => 'products', :column => 'product_name', :unique_members => true
end
end
+ dimension 'Time', :foreign_key => 'time_id', :type => 'TimeDimension' do
+ hierarchy :has_all => false, :primary_key => 'id' do
+ table 'time'
+ level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true, :level_type => 'TimeYears'
+ level 'Quarter', :column => 'quarter', :unique_members => false, :level_type => 'TimeQuarters'
+ level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false, :level_type => 'TimeMonths'
+ end
+ hierarchy 'Weekly', :has_all => false, :primary_key => 'id' do
+ table 'time'
+ level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true, :level_type => 'TimeYears'
+ level 'Week', :column => 'week_of_year', :type => 'Numeric', :unique_members => false, :level_type => 'TimeWeeks'
+ end
+ end
+ measure 'Unit Sales', :column => 'unit_sales', :aggregator => 'sum'
+ measure 'Store Sales', :column => 'store_sales', :aggregator => 'sum'
+ end
+end
+```
### Connection creation
When schema is defined it is necessary to establish OLAP connection to database. Here is example how to connect to MySQL database using the schema object that was defined previously:
- require "jdbc/mysql"
+```ruby
+require "jdbc/mysql"
- olap = Mondrian::OLAP::Connection.create(
- :driver => 'mysql',
- :host => 'localhost,
- :database => 'mondrian_test',
- :username => 'mondrian_user',
- :password => 'secret',
- :schema => schema
- )
+olap = Mondrian::OLAP::Connection.create(
+ :driver => 'mysql',
+ :host => 'localhost,
+ :database => 'mondrian_test',
+ :username => 'mondrian_user',
+ :password => 'secret',
+ :schema => schema
+)
+```
### MDX queries
Mondrian OLAP provides MDX query language. [Read more about MDX](http://mondrian.pentaho.com/documentation/mdx.php).
mondrian-olap allows executing of MDX queries, for example query for "Get sales amount and number of units (on columns) of all product families (on rows) sold in California during Q1 of 2010":
- result = olap.execute <<-MDX
- SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
- {[Products].children} ON ROWS
- FROM [Sales]
- WHERE ([Time].[2010].[Q1], [Customers].[USA].[CA])
- MDX
+```ruby
+result = olap.execute <<-MDX
+ SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
+ {[Products].children} ON ROWS
+ FROM [Sales]
+ WHERE ([Time].[2010].[Q1], [Customers].[USA].[CA])
+MDX
+```
which would correspond to the following SQL query:
@@ -136,36 +142,42 @@ which would correspond to the following SQL query:
and then get axis and cells of result object:
- result.axes_count # => 2
- result.column_names # => ["Unit Sales", "Store Sales"]
- result.column_full_names # => ["[Measures].[Unit Sales]", "[Measures].[Store Sales]"]
- result.row_names # => e.g. ["Drink", "Food", "Non-Consumable"]
- result.row_full_names # => e.g. ["[Products].[Drink]", "[Products].[Food]", "[Products].[Non-Consumable]"]
- result.values # => [[..., ...], [..., ...], [..., ...]]
- # (three rows, each row containing value for "unit sales" and "store sales")
+```ruby
+result.axes_count # => 2
+result.column_names # => ["Unit Sales", "Store Sales"]
+result.column_full_names # => ["[Measures].[Unit Sales]", "[Measures].[Store Sales]"]
+result.row_names # => e.g. ["Drink", "Food", "Non-Consumable"]
+result.row_full_names # => e.g. ["[Products].[Drink]", "[Products].[Food]", "[Products].[Non-Consumable]"]
+result.values # => [[..., ...], [..., ...], [..., ...]]
+ # (three rows, each row containing value for "unit sales" and "store sales")
+```
### Query builder methods
MDX queries could be built and executed also using Ruby methods in a similar way as ActiveRecord/Arel queries are made.
Previous MDX query can be executed as:
- olap.from('Sales').
- columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
- rows('[Products].children').
- where('[Time].[2010].[Q1]', '[Customers].[USA].[CA]').
- execute
+```ruby
+olap.from('Sales').
+columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
+rows('[Products].children').
+where('[Time].[2010].[Q1]', '[Customers].[USA].[CA]').
+execute
+```
Here is example of more complex query "Get sales amount and profit % of top 50 products cross-joined with USA and Canada country sales during Q1 of 2010":
- olap.from('Sales').
- with_member('[Measures].[ProfitPct]').
- as('Val((Measures.[Store Sales] - Measures.[Store Cost]) / Measures.[Store Sales])',
- :format_string => 'Percent').
- columns('[Measures].[Store Sales]', '[Measures].[ProfitPct]').
- rows('[Products].children').crossjoin('[Customers].[Canada]', '[Customers].[USA]').
- top_count(50, '[Measures].[Store Sales]').
- where('[Time].[2010].[Q1]').
- execute
+```ruby
+olap.from('Sales').
+with_member('[Measures].[ProfitPct]').
+ as('Val((Measures.[Store Sales] - Measures.[Store Cost]) / Measures.[Store Sales])',
+ :format_string => 'Percent').
+columns('[Measures].[Store Sales]', '[Measures].[ProfitPct]').
+rows('[Products].children').crossjoin('[Customers].[Canada]', '[Customers].[USA]').
+ top_count(50, '[Measures].[Store Sales]').
+where('[Time].[2010].[Q1]').
+execute
+```
See more examples of queries in `spec/query_spec.rb`.
@@ -175,33 +187,92 @@ Currently there are query builder methods just for most frequently used MDX func
mondrian-olap provides also methods for querying dimensions and members:
- cube = olap.cube('Sales')
- cube.dimension_names # => ['Measures', 'Customers', 'Products', 'Time']
- cube.dimensions # => array of dimension objects
- cube.dimension('Customers') # => customers dimension object
- cube.dimension('Time').hierarchy_names # => ['Time', 'Time.Weekly']
- cube.dimension('Time').hierarchies # => array of hierarchy objects
- cube.dimension('Customers').hierarchy # => default customers dimension hierarchy
- cube.dimension('Customers').hierarchy.level_names
- # => ['(All)', 'Country', 'State Province', 'City', 'Name']
- cube.dimension('Customers').hierarchy.levels
- # => array of hierarchy level objects
- cube.dimension('Customers').hierarchy.level('Country').members
- # => array of all level members
- cube.member('[Customers].[USA].[CA]') # => lookup member by full name
- cube.member('[Customers].[USA].[CA]').children
- # => get all children of member in deeper hierarchy level
- cube.member('[Customers].[USA]').descendants_at_level('City')
- # => get all descendants of member in specified hierarchy level
+```ruby
+cube = olap.cube('Sales')
+cube.dimension_names # => ['Measures', 'Customers', 'Products', 'Time']
+cube.dimensions # => array of dimension objects
+cube.dimension('Customers') # => customers dimension object
+cube.dimension('Time').hierarchy_names # => ['Time', 'Time.Weekly']
+cube.dimension('Time').hierarchies # => array of hierarchy objects
+cube.dimension('Customers').hierarchy # => default customers dimension hierarchy
+cube.dimension('Customers').hierarchy.level_names
+ # => ['(All)', 'Country', 'State Province', 'City', 'Name']
+cube.dimension('Customers').hierarchy.levels
+ # => array of hierarchy level objects
+cube.dimension('Customers').hierarchy.level('Country').members
+ # => array of all level members
+cube.member('[Customers].[USA].[CA]') # => lookup member by full name
+cube.member('[Customers].[USA].[CA]').children
+ # => get all children of member in deeper hierarchy level
+cube.member('[Customers].[USA]').descendants_at_level('City')
+ # => get all descendants of member in specified hierarchy level
+```
See more examples of dimension and member queries in `spec/cube_spec.rb`.
+### User defined MDX functions
+
+You can define new MDX functions using JavaScript, CoffeeScript or Ruby language that you can later use
+either in calculated member formulas or in MDX queries. Here are examples of user defined functions in Ruby:
+
+```ruby
+schema = Mondrian::OLAP::Schema.define do
+ # ... cube definitions ...
+ user_defined_function 'Factorial' do
+ ruby do
+ parameters :numeric
+ returns :numeric
+ def call(n)
+ n <= 1 ? 1 : n * call(n - 1)
+ end
+ end
+ end
+ user_defined_function 'UpperName' do
+ ruby do
+ parameters :member
+ returns :string
+ syntax :property
+ def call(member)
+ member.getName.upcase
+ end
+ end
+ end
+end
+```
+
+See more examples of user defined functions in `spec/schema_definition_spec.rb`.
+
+### Data access roles
+
+In schema you can define data access roles which can be selected for connection and which will limit access just to
+subset of measures and dimension members. Here is example of data access role definition:
+
+```ruby
+schema = Mondrian::OLAP::Schema.define do
+ # ... cube definitions ...
+ role 'California manager' do
+ schema_grant :access => 'none' do
+ cube_grant :cube => 'Sales', :access => 'all' do
+ dimension_grant :dimension => '[Measures]', :access => 'all'
+ hierarchy_grant :hierarchy => '[Customers]', :access => 'custom',
+ :top_level => '[Customers].[State Province]', :bottom_level => '[Customers].[City]' do
+ member_grant :member => '[Customers].[USA].[CA]', :access => 'all'
+ member_grant :member => '[Customers].[USA].[CA].[Los Angeles]', :access => 'none'
+ end
+ end
+ end
+ end
+end
+```
+
+See more examples of data access roles in `spec/connection_role_spec.rb`.
+
REQUIREMENTS
------------
-mondrian-olap gem is compatible with JRuby versions 1.5 and 1.6 (have not been tested with earlier versions). mondrian-olap works only with JRuby and not with other Ruby implementations as it includes Mondrian OLAP Java libraries.
+mondrian-olap gem is compatible with JRuby versions 1.6 and 1.7 and Java 6 and 7 VM. mondrian-olap works only with JRuby and not with other Ruby implementations as it includes Mondrian OLAP Java libraries.
-mondrian-olap currently supports MySQL, PostgreSQL, Oracle, LucidDB and SQL Server databases. When using MySQL, PostgreSQL or LucidDB databases then install jdbc-mysql, jdbc-postgres or jdbc-luciddb gem and require "jdbc/mysql", "jdbc/postgres" or "jdbc/luciddb" to load the corresponding JDBC database driver. When using Oracle then include Oracle JDBC driver (`ojdbc6.jar` for Java 6) in `CLASSPATH` or copy to `JRUBY_HOME/lib` or require it in application manually. When using SQL Server you can choose between the jTDS or Microsoft JDBC drivers. If you use jTDS require "jdbc/jtds". If you use the Microsoft JDBC driver include `sqljdbc.jar` or `sqljdbc4.jar` in `CLASSPATH` or copy to `JRUBY_HOME/lib` or require it in application manually.
+mondrian-olap currently supports MySQL, PostgreSQL, Oracle, LucidDB and Microsoft SQL Server databases. When using MySQL, PostgreSQL or LucidDB databases then install jdbc-mysql, jdbc-postgres or jdbc-luciddb gem and require "jdbc/mysql", "jdbc/postgres" or "jdbc/luciddb" to load the corresponding JDBC database driver. When using Oracle then include Oracle JDBC driver (`ojdbc6.jar` for Java 6) in `CLASSPATH` or copy to `JRUBY_HOME/lib` or require it in application manually. When using SQL Server you can choose between the jTDS or Microsoft JDBC drivers. If you use jTDS require "jdbc/jtds". If you use the Microsoft JDBC driver include `sqljdbc.jar` or `sqljdbc4.jar` in `CLASSPATH` or copy to `JRUBY_HOME/lib` or require it in application manually.
INSTALL
-------
6 RUNNING_TESTS.rdoc
View
@@ -20,12 +20,12 @@ Create tables with test data using
rake db:create_data
-or specify which database driver to use
+or specify which database driver to use
rake db:create_data MONDRIAN_DRIVER=mysql
rake db:create_data MONDRIAN_DRIVER=postgresql
rake db:create_data MONDRIAN_DRIVER=oracle
- rake db:create_data MONDRIAN_DRIVER=mssql
+ rake db:create_data MONDRIAN_DRIVER=mssql
rake db:create_data MONDRIAN_DRIVER=sqlserver
In case of LucidDB data are not generated and inserted directly into database but are imported from MySQL mondrian_test database (because inserting individual records into LucidDB is very inefficient). Therefore at first generate test data with mysql (using default database settings) and then run data creation task for LucidDB.
@@ -63,4 +63,4 @@ You can also run all tests on all databases with
== JRuby versions
-It is recommended to use RVM (http://rvm.beginrescueend.com) to run tests with different JRuby implementations. mondrian-olap is being tested with latest versions of JRuby 1.6 on Java 6.
+It is recommended to use RVM (http://rvm.beginrescueend.com) to run tests with different JRuby implementations. mondrian-olap is being tested with latest versions of JRuby 1.6 and 1.7 on Java 6 and 7.
2  VERSION
View
@@ -1 +1 @@
-0.3.0
+0.4.0
BIN  lib/mondrian/jars/eigenbase-properties.jar
View
Binary file not shown
BIN  lib/mondrian/jars/eigenbase-resgen.jar
View
Binary file not shown
BIN  lib/mondrian/jars/eigenbase-xom.jar
View
Binary file not shown
BIN  lib/mondrian/jars/mondrian.jar
View
Binary file not shown
BIN  lib/mondrian/jars/olap4j.jar
View
Binary file not shown
28 lib/mondrian/olap/connection.rb
View
@@ -82,12 +82,40 @@ def flush_schema_cache
raw_cache_control.flushSchemaCache
end
+ def available_role_names
+ @raw_connection.getAvailableRoleNames.to_a
+ end
+
+ def role_name
+ @raw_connection.getRoleName
+ end
+
+ def role_names
+ @raw_connection.getRoleNames.to_a
+ end
+
+ def role_name=(name)
+ Error.wrap_native_exception do
+ @raw_connection.setRoleName(name)
+ end
+ end
+
+ def role_names=(names)
+ Error.wrap_native_exception do
+ @raw_connection.setRoleNames(Array(names))
+ end
+ end
+
private
def connection_string
string = "jdbc:mondrian:Jdbc=#{quote_string(jdbc_uri)};JdbcDrivers=#{jdbc_driver};"
# by default use content checksum to reload schema when catalog has changed
string << "UseContentChecksum=true;" unless @params[:use_content_checksum] == false
+ if role = @params[:role] || @params[:roles]
+ roles = Array(role).map{|r| r && r.to_s.gsub(',', ',,')}.compact
+ string << "Role=#{quote_string(roles.join(','))};" unless roles.empty?
+ end
string << (@params[:catalog] ? "Catalog=#{catalog_uri}" : "CatalogContent=#{quote_string(catalog_content)}")
end
79 lib/mondrian/olap/schema.rb
View
@@ -20,7 +20,7 @@ def define(name = nil, attributes = {}, &block)
name, attributes = self.class.pre_process_arguments(name, attributes)
pre_process_attributes(attributes)
@attributes[:name] = name || @attributes[:name] || 'default' # otherwise connection with empty name fails
- instance_eval &block if block
+ instance_eval(&block) if block
self
end
@@ -50,7 +50,7 @@ def pre_process_attributes(attributes)
public
attributes :name, :description
- elements :cube, :user_defined_function
+ elements :cube, :role, :user_defined_function
class Cube < SchemaElement
attributes :name, :description,
@@ -303,6 +303,81 @@ class AggExclude < SchemaElement
data_dictionary_names :name, :pattern
end
+ class Role < SchemaElement
+ attributes :name
+ elements :schema_grant, :union
+ end
+
+ class SchemaGrant < SchemaElement
+ # access may be "all", "all_dimensions", "custom" or "none".
+ # If access is "all_dimensions", the role has access to all dimensions but still needs explicit access to cubes.
+ # If access is "custom", no access will be inherited by cubes for which no explicit rule is set.
+ # If access is "all_dimensions", an implicut access is given to all dimensions of the schema's cubes,
+ # provided the cube's access attribute is either "custom" or "all"
+ attributes :access
+ elements :cube_grant
+ end
+
+ class CubeGrant < SchemaElement
+ # access may be "all", "custom", or "none".
+ # If access is "custom", no access will be inherited by the dimensions of this cube,
+ # unless the parent SchemaGrant is set to "all_dimensions"
+ attributes :access,
+ # The unique name of the cube
+ :cube
+ elements :dimension_grant, :hierarchy_grant
+ end
+
+ class DimensionGrant < SchemaElement
+ # access may be "all", "custom" or "none".
+ # Note that a role is implicitly given access to a dimension when it is given "all" acess to a cube.
+ # If access is "custom", no access will be inherited by the hierarchies of this dimension.
+ # If the parent schema access is "all_dimensions", this timension will inherit access "all".
+ # See also the "all_dimensions" option of the "SchemaGrant" element.
+ attributes :access,
+ # The unique name of the dimension
+ :dimension
+ end
+
+ class HierarchyGrant < SchemaElement
+ # access may be "all", "custom" or "none".
+ # If access is "custom", you may also specify the attributes :top_level, :bottom_level, and the member grants.
+ # If access is "custom", the child levels of this hierarchy will not inherit access rights from this hierarchy,
+ # should there be no explicit rules defined for the said child level.
+ attributes :access,
+ # The unique name of the hierarchy
+ :hierarchy,
+ # Unique name of the highest level of the hierarchy from which this role is allowed to see members.
+ # May only be specified if the HierarchyGrant.access is "custom".
+ # If not specified, role can see members up to the top level.
+ :top_level,
+ # Unique name of the lowest level of the hierarchy from which this role is allowed to see members.
+ # May only be specified if the HierarchyGrant.access is "custom".
+ # If not specified, role can see members down to the leaf level.
+ :bottom_level,
+ # Policy which determines how cell values are calculated if not all of the children of the current cell
+ # are visible to the current role.
+ # Allowable values are "full" (the default), "partial", and "hidden".
+ :rollup_policy
+ elements :member_grant
+ end
+
+ class MemberGrant < SchemaElement
+ # The children of this member inherit that access.
+ # You can implicitly see a member if you can see any of its children.
+ attributes :access,
+ # The unique name of the member
+ :member
+ end
+
+ class Union < SchemaElement
+ elements :role_usage
+ end
+
+ class RoleUsage < SchemaElement
+ attributes :role_name
+ end
+
end
end
end
2  lib/mondrian/olap/schema_element.rb
View
@@ -22,7 +22,7 @@ def initialize(name = nil, attributes = {}, &block)
instance_variable_set("@#{pluralize(element)}", [])
end
@xml_fragments = []
- instance_eval &block if block
+ instance_eval(&block) if block
end
def self.attributes(*names)
2  lib/mondrian/olap/schema_udf.rb
View
@@ -221,7 +221,7 @@ def ruby(*options, &block)
self.class.const_set(udf_class_name, udf_class) if udf_class_name
end
udf_class.function_name = name
- udf_class.class_eval &block
+ udf_class.class_eval(&block)
udf_java_class = udf_class.become_java!(false)
class_name udf_java_class.getName
74 mondrian-olap.gemspec
View
@@ -4,16 +4,15 @@
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
- s.name = %q{mondrian-olap}
- s.version = "0.3.0"
- s.platform = %q{java}
+ s.name = "mondrian-olap"
+ s.version = "0.4.0"
+ s.platform = "java"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
- s.authors = [%q{Raimonds Simanovskis}]
- s.date = %q{2012-01-12}
- s.description = %q{JRuby gem for performing multidimensional queries of relational database data using Mondrian OLAP Java library
-}
- s.email = %q{raimonds.simanovskis@gmail.com}
+ s.authors = ["Raimonds Simanovskis"]
+ s.date = "2012-12-03"
+ s.description = "JRuby gem for performing multidimensional queries of relational database data using Mondrian OLAP Java library\n"
+ s.email = "raimonds.simanovskis@gmail.com"
s.extra_rdoc_files = [
"README.md"
]
@@ -45,12 +44,14 @@ Gem::Specification.new do |s|
"lib/mondrian/olap.rb",
"lib/mondrian/olap/connection.rb",
"lib/mondrian/olap/cube.rb",
+ "lib/mondrian/olap/error.rb",
"lib/mondrian/olap/query.rb",
"lib/mondrian/olap/result.rb",
"lib/mondrian/olap/schema.rb",
"lib/mondrian/olap/schema_element.rb",
"lib/mondrian/olap/schema_udf.rb",
"mondrian-olap.gemspec",
+ "spec/connection_role_spec.rb",
"spec/connection_spec.rb",
"spec/cube_spec.rb",
"spec/fixtures/MondrianTest.xml",
@@ -62,20 +63,10 @@ Gem::Specification.new do |s|
"spec/spec_helper.rb",
"spec/support/matchers/be_like.rb"
]
- s.homepage = %q{http://github.com/rsim/mondrian-olap}
- s.require_paths = [%q{lib}]
- s.rubygems_version = %q{1.8.9}
- s.summary = %q{JRuby API for Mondrian OLAP Java library}
- s.test_files = [
- "spec/connection_spec.rb",
- "spec/cube_spec.rb",
- "spec/mondrian_spec.rb",
- "spec/query_spec.rb",
- "spec/rake_tasks.rb",
- "spec/schema_definition_spec.rb",
- "spec/spec_helper.rb",
- "spec/support/matchers/be_like.rb"
- ]
+ s.homepage = "http://github.com/rsim/mondrian-olap"
+ s.require_paths = ["lib"]
+ s.rubygems_version = "1.8.24"
+ s.summary = "JRuby API for Mondrian OLAP Java library"
if s.respond_to? :specification_version then
s.specification_version = 3
@@ -83,52 +74,55 @@ Gem::Specification.new do |s|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<nokogiri>, ["~> 1.5.0"])
s.add_development_dependency(%q<jruby-openssl>, [">= 0"])
- s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
s.add_development_dependency(%q<rdoc>, [">= 0"])
- s.add_development_dependency(%q<rspec>, ["~> 2.5"])
+ s.add_development_dependency(%q<rspec>, ["~> 2.10"])
s.add_development_dependency(%q<autotest>, [">= 0"])
s.add_development_dependency(%q<jdbc-mysql>, [">= 0"])
s.add_development_dependency(%q<jdbc-postgres>, [">= 0"])
s.add_development_dependency(%q<jdbc-luciddb>, [">= 0"])
s.add_development_dependency(%q<jdbc-jtds>, [">= 0"])
- s.add_development_dependency(%q<activerecord>, ["= 3.0.10"])
- s.add_development_dependency(%q<activerecord-jdbc-adapter>, ["= 1.1.1"])
+ s.add_development_dependency(%q<activerecord>, ["= 3.2.8"])
+ s.add_development_dependency(%q<activerecord-jdbc-adapter>, ["= 1.2.2"])
s.add_development_dependency(%q<activerecord-oracle_enhanced-adapter>, [">= 0"])
- s.add_development_dependency(%q<coffee-script>, [">= 0"])
- s.add_development_dependency(%q<therubyrhino>, [">= 0"])
+ s.add_development_dependency(%q<coffee-script>, ["~> 2.2.0"])
+ s.add_development_dependency(%q<therubyrhino>, ["~> 1.73.1"])
+ s.add_development_dependency(%q<pry>, [">= 0"])
else
s.add_dependency(%q<nokogiri>, ["~> 1.5.0"])
s.add_dependency(%q<jruby-openssl>, [">= 0"])
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
s.add_dependency(%q<rdoc>, [">= 0"])
- s.add_dependency(%q<rspec>, ["~> 2.5"])
+ s.add_dependency(%q<rspec>, ["~> 2.10"])
s.add_dependency(%q<autotest>, [">= 0"])
s.add_dependency(%q<jdbc-mysql>, [">= 0"])
s.add_dependency(%q<jdbc-postgres>, [">= 0"])
s.add_dependency(%q<jdbc-luciddb>, [">= 0"])
s.add_dependency(%q<jdbc-jtds>, [">= 0"])
- s.add_dependency(%q<activerecord>, ["= 3.0.10"])
- s.add_dependency(%q<activerecord-jdbc-adapter>, ["= 1.1.1"])
+ s.add_dependency(%q<activerecord>, ["= 3.2.8"])
+ s.add_dependency(%q<activerecord-jdbc-adapter>, ["= 1.2.2"])
s.add_dependency(%q<activerecord-oracle_enhanced-adapter>, [">= 0"])
- s.add_dependency(%q<coffee-script>, [">= 0"])
- s.add_dependency(%q<therubyrhino>, [">= 0"])
+ s.add_dependency(%q<coffee-script>, ["~> 2.2.0"])
+ s.add_dependency(%q<therubyrhino>, ["~> 1.73.1"])
+ s.add_dependency(%q<pry>, [">= 0"])
end
else
s.add_dependency(%q<nokogiri>, ["~> 1.5.0"])
s.add_dependency(%q<jruby-openssl>, [">= 0"])
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
s.add_dependency(%q<rdoc>, [">= 0"])
- s.add_dependency(%q<rspec>, ["~> 2.5"])
+ s.add_dependency(%q<rspec>, ["~> 2.10"])
s.add_dependency(%q<autotest>, [">= 0"])
s.add_dependency(%q<jdbc-mysql>, [">= 0"])
s.add_dependency(%q<jdbc-postgres>, [">= 0"])
s.add_dependency(%q<jdbc-luciddb>, [">= 0"])
s.add_dependency(%q<jdbc-jtds>, [">= 0"])
- s.add_dependency(%q<activerecord>, ["= 3.0.10"])
- s.add_dependency(%q<activerecord-jdbc-adapter>, ["= 1.1.1"])
+ s.add_dependency(%q<activerecord>, ["= 3.2.8"])
+ s.add_dependency(%q<activerecord-jdbc-adapter>, ["= 1.2.2"])
s.add_dependency(%q<activerecord-oracle_enhanced-adapter>, [">= 0"])
- s.add_dependency(%q<coffee-script>, [">= 0"])
- s.add_dependency(%q<therubyrhino>, [">= 0"])
+ s.add_dependency(%q<coffee-script>, ["~> 2.2.0"])
+ s.add_dependency(%q<therubyrhino>, ["~> 1.73.1"])
+ s.add_dependency(%q<pry>, [">= 0"])
end
end
130 spec/connection_role_spec.rb
View
@@ -0,0 +1,130 @@
+require "spec_helper"
+
+describe "Connection role" do
+
+ describe "create connection" do
+ before(:each) do
+ @role_name = role_name = 'California manager'
+ @role_name2 = role_name2 = 'Dummy, with comma'
+ @schema = Mondrian::OLAP::Schema.define do
+ cube 'Sales' do
+ table 'sales'
+ dimension 'Gender', :foreign_key => 'customer_id' do
+ hierarchy :has_all => true, :primary_key => 'id' do
+ table 'customers'
+ level 'Gender', :column => 'gender', :unique_members => true
+ end
+ end
+ dimension 'Customers', :foreign_key => 'customer_id' do
+ hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
+ table 'customers'
+ level 'Country', :column => 'country', :unique_members => true
+ level 'State Province', :column => 'state_province', :unique_members => true
+ level 'City', :column => 'city', :unique_members => false
+ level 'Name', :column => 'fullname', :unique_members => true
+ end
+ end
+ dimension 'Time', :foreign_key => 'time_id' do
+ hierarchy :has_all => false, :primary_key => 'id' do
+ table 'time'
+ level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true
+ level 'Quarter', :column => 'quarter', :unique_members => false
+ level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false
+ end
+ end
+ measure 'Unit Sales', :column => 'unit_sales', :aggregator => 'sum'
+ measure 'Store Sales', :column => 'store_sales', :aggregator => 'sum'
+ end
+ role role_name do
+ schema_grant :access => 'none' do
+ cube_grant :cube => 'Sales', :access => 'all' do
+ dimension_grant :dimension => '[Measures]', :access => 'all'
+ hierarchy_grant :hierarchy => '[Customers]', :access => 'custom',
+ :top_level => '[Customers].[State Province]', :bottom_level => '[Customers].[City]' do
+ member_grant :member => '[Customers].[USA].[CA]', :access => 'all'
+ member_grant :member => '[Customers].[USA].[CA].[Los Angeles]', :access => 'none'
+ end
+ end
+ end
+ end
+ role role_name2
+
+ # to test that Role elements are generated before UserDefinedFunction
+ user_defined_function 'Factorial' do
+ ruby do
+ parameters :numeric
+ returns :numeric
+ def call(n)
+ n <= 1 ? 1 : n * call(n - 1)
+ end
+ end
+ end
+ end
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
+ end
+
+ it "should connect" do
+ @olap.should be_connected
+ end
+
+ it "should get available role names" do
+ @olap.available_role_names.should == [@role_name, @role_name2]
+ end
+
+ it "should not get role name if not set" do
+ @olap.role_name.should be_nil
+ @olap.role_names.should be_empty
+ end
+
+ it "should set and get role name" do
+ @olap.role_name = @role_name
+ @olap.role_name.should == @role_name
+ @olap.role_names.should == [@role_name]
+ end
+
+ it "should raise error when invalid role name is set" do
+ expect {
+ @olap.role_name = 'invalid'
+ }.to raise_error {|e|
+ e.should be_kind_of(Mondrian::OLAP::Error)
+ e.message.should == "org.olap4j.OlapException: Unknown role 'invalid'"
+ e.root_cause_message.should == "Unknown role 'invalid'"
+ }
+ end
+
+ it "should set and get several role names" do
+ @olap.role_names = [@role_name, @role_name2]
+ @olap.role_name.should == "[#{@role_name}, #{@role_name2}]"
+ @olap.role_names.should == [@role_name, @role_name2]
+ end
+
+ it "should not get non-visible member" do
+ @cube = @olap.cube('Sales')
+ @cube.member('[Customers].[USA].[CA].[Los Angeles]').should_not be_nil
+ @olap.role_name = @role_name
+ @cube.member('[Customers].[USA].[CA].[Los Angeles]').should be_nil
+ end
+
+ # TODO: investigate why role name is not returned when set in connection string
+ # it "should set role name from connection parameters" do
+ # @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema,
+ # :role => @role_name)
+ # @olap.role_name.should == @role_name
+ # end
+
+ it "should not get non-visible member when role name set in connection parameters" do
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema,
+ :role => @role_name)
+ @cube = @olap.cube('Sales')
+ @cube.member('[Customers].[USA].[CA].[Los Angeles]').should be_nil
+ end
+
+ it "should not get non-visible member when several role names set in connection parameters" do
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema,
+ :roles => [@role_name, @role_name2])
+ @cube = @olap.cube('Sales')
+ @cube.member('[Customers].[USA].[CA].[Los Angeles]').should be_nil
+ end
+
+ end
+end
4 spec/rake_tasks.rb
View
@@ -82,7 +82,7 @@
task :define_models => :require_spec_helper do
unless MONDRIAN_DRIVER == 'luciddb'
class TimeDimension < ActiveRecord::Base
- set_table_name "time"
+ self.table_name = "time"
validates_presence_of :the_date
before_create do
self.the_day = the_date.strftime("%A")
@@ -102,7 +102,7 @@ class ProductClass < ActiveRecord::Base
class Customer < ActiveRecord::Base
end
class Sales < ActiveRecord::Base
- set_table_name "sales"
+ self.table_name = "sales"
belongs_to :time_by_day
belongs_to :product
belongs_to :customer
36 spec/schema_definition_spec.rb
View
@@ -1128,6 +1128,42 @@ def call(member, dummy)
end
end
+
+ describe "Roles" do
+ it "should render XML" do
+ @schema.define do
+ role 'California manager' do
+ schema_grant :access => 'none' do
+ cube_grant :cube => 'Sales', :access => 'all' do
+ dimension_grant :dimension => '[Measures]', :access => 'all'
+ hierarchy_grant :hierarchy => '[Customers]', :access => 'custom',
+ :top_level => '[Customers].[State Province]', :bottom_level => '[Customers].[City]' do
+ member_grant :member => '[Customers].[USA].[CA]', :access => 'all'
+ member_grant :member => '[Customers].[USA].[CA].[Los Angeles]', :access => 'none'
+ end
+ end
+ end
+ end
+ end
+ @schema.to_xml.should be_like <<-XML
+ <?xml version="1.0"?>
+ <Schema name="default">
+ <Role name="California manager">
+ <SchemaGrant access="none">
+ <CubeGrant access="all" cube="Sales">
+ <DimensionGrant access="all" dimension="[Measures]"/>
+ <HierarchyGrant access="custom" bottomLevel="[Customers].[City]" hierarchy="[Customers]" topLevel="[Customers].[State Province]">
+ <MemberGrant access="all" member="[Customers].[USA].[CA]"/>
+ <MemberGrant access="none" member="[Customers].[USA].[CA].[Los Angeles]"/>
+ </HierarchyGrant>
+ </CubeGrant>
+ </SchemaGrant>
+ </Role>
+ </Schema>
+ XML
+ end
+ end
+
end
describe "connection with schema" do

No commit comments for this range

Something went wrong with that request. Please try again.