rubiks 0.0.3 → 0.0.4
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.
- data/.rspec +4 -0
- data/.travis.yml +6 -0
- data/Gemfile +17 -2
- data/Guardfile +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +9 -4
- data/Rakefile +3 -8
- data/lib/rubiks/nodes/annotated_node.rb +20 -0
- data/lib/rubiks/nodes/cube.rb +77 -0
- data/lib/rubiks/nodes/dimension.rb +54 -0
- data/lib/rubiks/nodes/hierarchy.rb +55 -0
- data/lib/rubiks/nodes/level.rb +29 -0
- data/lib/rubiks/nodes/measure.rb +66 -0
- data/lib/rubiks/nodes/schema.rb +61 -0
- data/lib/rubiks/nodes/validated_node.rb +47 -0
- data/lib/rubiks/version.rb +2 -2
- data/lib/rubiks.rb +6 -8
- data/rubiks.gemspec +5 -13
- data/spec/examples/mondrian_xml_example_spec.rb +91 -0
- data/spec/rubiks/nodes/annotated_node_spec.rb +24 -0
- data/spec/rubiks/nodes/cube_spec.rb +39 -0
- data/spec/rubiks/nodes/dimension_spec.rb +26 -0
- data/spec/rubiks/nodes/hierarchy_spec.rb +27 -0
- data/spec/rubiks/nodes/level_spec.rb +26 -0
- data/spec/rubiks/nodes/measure_spec.rb +31 -0
- data/spec/rubiks/nodes/schema_spec.rb +59 -0
- data/spec/rubiks/nodes/validated_node_spec.rb +49 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/matchers/be_like.rb +24 -0
- data/spec/support/schema_context.rb +46 -0
- metadata +45 -144
- data/.simplecov +0 -6
- data/examples/finance/.rvmrc +0 -1
- data/examples/finance/Gemfile +0 -11
- data/examples/finance/app.rb +0 -14
- data/examples/finance/database.yml +0 -5
- data/examples/finance/domain.rb +0 -44
- data/examples/finance/setup +0 -46
- data/lib/rubiks/cube.rb +0 -95
- data/lib/rubiks/dimension.rb +0 -23
- data/lib/rubiks/hierarchy.rb +0 -15
- data/lib/rubiks/transformers/lookup_transformer.rb +0 -101
- data/test/rubiks/test_cube.rb +0 -15
- data/test/rubiks/test_dimension.rb +0 -11
- data/test/test_helper.rb +0 -6
data/examples/finance/domain.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
module Dimensions
|
2
|
-
class Account < ActiveRecord::Base
|
3
|
-
include Rubiks::Dimension
|
4
|
-
|
5
|
-
hierarchy 'Asset/Liability' do
|
6
|
-
level :asset_liability
|
7
|
-
level :account_type
|
8
|
-
end
|
9
|
-
|
10
|
-
hierarchy 'Institution' do
|
11
|
-
level :institution
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
class Customer < ActiveRecord::Base
|
16
|
-
include Rubiks::Dimension
|
17
|
-
|
18
|
-
hierarchy 'Gender' do
|
19
|
-
level :gender
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class Date < ActiveRecord::Base
|
24
|
-
include Rubiks::Dimension
|
25
|
-
|
26
|
-
hierarchy 'Date' do
|
27
|
-
level :year
|
28
|
-
level :quarter
|
29
|
-
level :month
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
module Facts
|
35
|
-
class AccountSnapshot < ActiveRecord::Base
|
36
|
-
include Rubiks::Fact
|
37
|
-
|
38
|
-
dimension :account
|
39
|
-
dimension :customer
|
40
|
-
dimension :date
|
41
|
-
|
42
|
-
measure :balance
|
43
|
-
end
|
44
|
-
end
|
data/examples/finance/setup
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# Requires PostgreSQL
|
3
|
-
# Setup the finance_development DB
|
4
|
-
|
5
|
-
require 'bundler'
|
6
|
-
Bundler.require
|
7
|
-
|
8
|
-
config = YAML.load_file('./database.yml')
|
9
|
-
|
10
|
-
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres'))
|
11
|
-
ActiveRecord::Base.connection.create_database(config['database'])
|
12
|
-
|
13
|
-
ActiveRecord::Base.establish_connection(config)
|
14
|
-
|
15
|
-
ActiveRecord::Migration.create_table :accounts do |t|
|
16
|
-
t.string :asset_liability
|
17
|
-
t.string :account_type
|
18
|
-
t.string :institution
|
19
|
-
end
|
20
|
-
|
21
|
-
ActiveRecord::Migration.create_table :dates do |t|
|
22
|
-
t.integer :year
|
23
|
-
t.integer :quarter
|
24
|
-
t.integer :month
|
25
|
-
t.integer :day
|
26
|
-
end
|
27
|
-
|
28
|
-
ActiveRecord::Migration.create_table :customers do |t|
|
29
|
-
t.string :name
|
30
|
-
t.string :gender
|
31
|
-
end
|
32
|
-
|
33
|
-
ActiveRecord::Migration.create_table :account_snapshots do |t|
|
34
|
-
t.references :account
|
35
|
-
t.references :date
|
36
|
-
t.references :customer
|
37
|
-
t.decimal :balance
|
38
|
-
end
|
39
|
-
|
40
|
-
require './domain'
|
41
|
-
|
42
|
-
account = Dimensions::Account.create(:asset_liability => 'ASSET', :account_type => 'Savings', :institution => 'ACU - Awesome Credit Union')
|
43
|
-
customer = Dimensions::Customer.create(:name => 'JohnnyT', :gender => 'Male')
|
44
|
-
date = Dimesnions::Date.create(:year => 2012, :quarter => 4, :month => 11, :day => 31)
|
45
|
-
|
46
|
-
Facts::AccountSnapshot.create(:account => account, :customer => customer, :date => date, :balance => 100.00)
|
data/lib/rubiks/cube.rb
DELETED
@@ -1,95 +0,0 @@
|
|
1
|
-
module Rubiks
|
2
|
-
module Cube
|
3
|
-
def self.included(klass)
|
4
|
-
klass.extend Rubiks::Cube::ClassMethods
|
5
|
-
end
|
6
|
-
|
7
|
-
module ClassMethods
|
8
|
-
def dimension(name)
|
9
|
-
model_name = name.to_s
|
10
|
-
dimensions << model_name
|
11
|
-
belongs_to model_name, :class_name => "Dimensions::#{model_name.classify}"
|
12
|
-
end
|
13
|
-
|
14
|
-
def dimensions
|
15
|
-
@dimensions ||= []
|
16
|
-
end
|
17
|
-
|
18
|
-
def measure(name)
|
19
|
-
measures << name
|
20
|
-
end
|
21
|
-
|
22
|
-
def measures
|
23
|
-
@measures ||= []
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def mdx(query)
|
28
|
-
res = olap.execute(query)
|
29
|
-
output = []
|
30
|
-
output << res.column_full_names
|
31
|
-
res.values.each{ |v| output << v }
|
32
|
-
output.join("\n")
|
33
|
-
end
|
34
|
-
|
35
|
-
def to_s
|
36
|
-
"#<#{self.class.name}>"
|
37
|
-
end
|
38
|
-
|
39
|
-
def inspect
|
40
|
-
"#<#{self.class.name} dims: #{self.class.dimensions.inspect}>"
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def olap
|
47
|
-
@olap ||= Mondrian::OLAP::Connection.create(mondrian_config)
|
48
|
-
end
|
49
|
-
|
50
|
-
def mondrian_config
|
51
|
-
@mondrian_config ||= begin
|
52
|
-
ar_config = ActiveRecord::Base.connection.config
|
53
|
-
|
54
|
-
{
|
55
|
-
:driver => ar_config[:adapter],
|
56
|
-
:host => ar_config[:host],
|
57
|
-
:database => ar_config[:database],
|
58
|
-
:username => ar_config[:username],
|
59
|
-
:password => ar_config[:password],
|
60
|
-
:schema => mondrian_schema
|
61
|
-
}
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def mondrian_schema
|
66
|
-
rubiks_cube = self
|
67
|
-
@mondrian_schema ||= Mondrian::OLAP::Schema.define do
|
68
|
-
cube rubiks_cube.class.name do
|
69
|
-
table rubiks_cube.class.table_name
|
70
|
-
|
71
|
-
rubiks_cube.class.reflections.each do |name, reflection|
|
72
|
-
dimension reflection.name.titleize, :foreign_key => reflection.foreign_key do
|
73
|
-
|
74
|
-
reflection.class_name.constantize.hierarchies.each do |h|
|
75
|
-
hierarchy :has_all => true, :primary_key => :id do
|
76
|
-
table reflection.table_name
|
77
|
-
|
78
|
-
h.levels.each do |l|
|
79
|
-
level l.to_s.titleize, :column => l
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
rubiks_cube.class.measures.each do |m|
|
88
|
-
measure m.to_s.titleize, :column => m, :aggregator => 'avg'
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
end
|
95
|
-
end
|
data/lib/rubiks/dimension.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
module Rubiks
|
2
|
-
module Dimension
|
3
|
-
def self.included(klass)
|
4
|
-
klass.extend Rubiks::Dimension::ClassMethods
|
5
|
-
end
|
6
|
-
|
7
|
-
module ClassMethods
|
8
|
-
def hierarchy(name, &block)
|
9
|
-
new_hierarchy = Hierarchy.new(name)
|
10
|
-
new_hierarchy.instance_eval(&block) if block_given?
|
11
|
-
hierarchies << new_hierarchy
|
12
|
-
end
|
13
|
-
|
14
|
-
def hierarchies
|
15
|
-
@hierarchies ||= []
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def hierarchies
|
20
|
-
self.class.hierarchies
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/lib/rubiks/hierarchy.rb
DELETED
@@ -1,101 +0,0 @@
|
|
1
|
-
module Rubiks
|
2
|
-
module Transformers
|
3
|
-
module LookupTransformer
|
4
|
-
##
|
5
|
-
# Public instance methods
|
6
|
-
#
|
7
|
-
# Looks up the member or creates a new member if missing
|
8
|
-
# @param[ActiveRecord Class] The class to lookup
|
9
|
-
# @param[String] The external natural key (PK)
|
10
|
-
# @return[Object] The found or created member
|
11
|
-
def lookup_member(klass, natural_key)
|
12
|
-
model_name = klass.name.underscore
|
13
|
-
cache_key = "rubiks.lookup.#{model_name}.#{natural_key}"
|
14
|
-
|
15
|
-
if id = cache.read(cache_key)
|
16
|
-
klass.find_by_id(id)
|
17
|
-
|
18
|
-
elsif existing_member = klass.where(natural_key_field => natural_key).first
|
19
|
-
cache.write(cache_key, existing_member.id)
|
20
|
-
existing_member
|
21
|
-
|
22
|
-
else
|
23
|
-
new_member = klass.new
|
24
|
-
new_member[natural_key_field] = natural_key
|
25
|
-
|
26
|
-
new_member.save!
|
27
|
-
cache.write(cache_key, new_member.id)
|
28
|
-
new_member
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Looks up the surrogate key based off a natural key
|
33
|
-
# @param[ActiveRecord Class] The class to lookup
|
34
|
-
# @param[String] The external natural key (PK)
|
35
|
-
# @return[Integer] The surrogate key of the found or created member
|
36
|
-
def lookup(klass, natural_key)
|
37
|
-
model_name = klass.name.underscore
|
38
|
-
cache_key = "rubiks.lookup.#{model_name}.#{natural_key}"
|
39
|
-
|
40
|
-
if id = cache.read(cache_key)
|
41
|
-
id
|
42
|
-
|
43
|
-
elsif existing_member = klass.where(natural_key_field => natural_key).first
|
44
|
-
cache.write(cache_key, existing_member.id)
|
45
|
-
existing_member.id
|
46
|
-
|
47
|
-
else
|
48
|
-
new_member = klass.new
|
49
|
-
new_member[natural_key_field] = natural_key
|
50
|
-
|
51
|
-
new_member.save!
|
52
|
-
cache.write(cache_key, new_member.id)
|
53
|
-
new_member.id
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def lookup_date(input)
|
58
|
-
date = if input == :today
|
59
|
-
Date.today
|
60
|
-
elsif input == :yesterday
|
61
|
-
1.day.ago
|
62
|
-
elsif input.kind_of? Integer
|
63
|
-
Time.at(input)
|
64
|
-
else
|
65
|
-
Date.parse(input)
|
66
|
-
end
|
67
|
-
|
68
|
-
date.strftime('%Y%m%d').to_i
|
69
|
-
rescue
|
70
|
-
-1
|
71
|
-
end
|
72
|
-
|
73
|
-
def lookup_time(input)
|
74
|
-
date = if input == :now
|
75
|
-
Time.now
|
76
|
-
elsif input.kind_of? Integer
|
77
|
-
Time.at(input)
|
78
|
-
else
|
79
|
-
Date.parse(input)
|
80
|
-
end
|
81
|
-
|
82
|
-
# 1 HH MM SS 000
|
83
|
-
# 11:59 PM = 1235959000
|
84
|
-
date.strftime('1%H%M%S000').to_i
|
85
|
-
rescue
|
86
|
-
-1
|
87
|
-
end
|
88
|
-
|
89
|
-
def natural_key_field
|
90
|
-
:natural_key
|
91
|
-
end
|
92
|
-
|
93
|
-
private
|
94
|
-
|
95
|
-
def cache
|
96
|
-
@cache ||= Rails.cache
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
data/test/rubiks/test_cube.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class TestCube < MiniTest::Unit::TestCase
|
4
|
-
def setup
|
5
|
-
@subject = Rubiks::Cube.new
|
6
|
-
end
|
7
|
-
|
8
|
-
def test_for_dimensions
|
9
|
-
assert_respond_to @subject, :dimensions
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_for_measures
|
13
|
-
assert_respond_to @subject, :measures
|
14
|
-
end
|
15
|
-
end
|