rubiks 0.0.1 → 0.0.2
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/Gemfile +0 -3
- data/examples/finance/.rvmrc +1 -0
- data/examples/finance/Gemfile +11 -0
- data/examples/finance/app.rb +14 -0
- data/examples/finance/database.yml +5 -0
- data/examples/finance/domain.rb +44 -0
- data/examples/finance/setup +46 -0
- data/lib/rubiks/cube.rb +89 -3
- data/lib/rubiks/dimension.rb +18 -1
- data/lib/rubiks/hierarchy.rb +15 -0
- data/lib/rubiks/transformers/lookup_transformer.rb +81 -0
- data/lib/rubiks/version.rb +1 -1
- data/lib/rubiks.rb +3 -6
- data/rubiks.gemspec +5 -3
- metadata +46 -12
data/Gemfile
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use jruby-1.7.0
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
ActiveRecord::Base.establish_connection(YAML.load_file('./database.yml'))
|
6
|
+
require './domain'
|
7
|
+
|
8
|
+
query = "SELECT {[Measures].[Balance]} ON COLUMNS, {[Customers].children} ON ROWS FROM [CubeAccountSnapshots] WHERE ([Date].[2012].[4])"
|
9
|
+
|
10
|
+
cas = CubeAccountSnapshot.last
|
11
|
+
|
12
|
+
binding.pry
|
13
|
+
|
14
|
+
cas.mdx query
|
@@ -0,0 +1,44 @@
|
|
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
|
@@ -0,0 +1,46 @@
|
|
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
CHANGED
@@ -1,9 +1,95 @@
|
|
1
1
|
module Rubiks
|
2
|
-
|
3
|
-
def
|
2
|
+
module Cube
|
3
|
+
def self.included(klass)
|
4
|
+
klass.extend Rubiks::Cube::ClassMethods
|
4
5
|
end
|
5
6
|
|
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
|
7
92
|
end
|
93
|
+
|
8
94
|
end
|
9
95
|
end
|
data/lib/rubiks/dimension.rb
CHANGED
@@ -1,6 +1,23 @@
|
|
1
1
|
module Rubiks
|
2
|
-
|
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
|
+
|
3
19
|
def hierarchies
|
20
|
+
self.class.hierarchies
|
4
21
|
end
|
5
22
|
end
|
6
23
|
end
|
@@ -0,0 +1,81 @@
|
|
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 => 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 = 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 => 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 = 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
|
+
private
|
74
|
+
|
75
|
+
def cache
|
76
|
+
@cache ||= Rails.cache
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/rubiks/version.rb
CHANGED
data/lib/rubiks.rb
CHANGED
@@ -3,12 +3,9 @@ require 'rubiks/version'
|
|
3
3
|
module Rubiks
|
4
4
|
autoload :Cube, 'rubiks/cube'
|
5
5
|
autoload :Dimension, 'rubiks/dimension'
|
6
|
+
autoload :Hierarchy, 'rubiks/hierarchy'
|
6
7
|
|
7
|
-
module
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
module Dimensions
|
12
|
-
# autoload :Base, 'rubiks/dimensions/base'
|
8
|
+
module Transformers
|
9
|
+
autoload :LookupTransformer, 'rubiks/transformers/lookup_transformer'
|
13
10
|
end
|
14
11
|
end
|
data/rubiks.gemspec
CHANGED
@@ -19,11 +19,13 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.add_dependency 'arel'
|
21
21
|
|
22
|
-
gem.add_development_dependency '
|
23
|
-
gem.add_development_dependency 'minitest'
|
24
|
-
gem.add_development_dependency 'rb-fsevent'
|
22
|
+
gem.add_development_dependency 'awesome_print'
|
25
23
|
gem.add_development_dependency 'guard'
|
26
24
|
gem.add_development_dependency 'guard-minitest'
|
25
|
+
gem.add_development_dependency 'minitest'
|
26
|
+
gem.add_development_dependency 'pry'
|
27
|
+
gem.add_development_dependency 'rake'
|
28
|
+
gem.add_development_dependency 'rb-fsevent'
|
27
29
|
gem.add_development_dependency 'simplecov'
|
28
30
|
gem.add_development_dependency 'simplecov-gem-adapter'
|
29
31
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubiks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: arel
|
@@ -28,7 +28,39 @@ dependencies:
|
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
31
|
+
name: awesome_print
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: guard
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: guard-minitest
|
32
64
|
requirement: !ruby/object:Gem::Requirement
|
33
65
|
none: false
|
34
66
|
requirements:
|
@@ -60,7 +92,7 @@ dependencies:
|
|
60
92
|
- !ruby/object:Gem::Version
|
61
93
|
version: '0'
|
62
94
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
95
|
+
name: pry
|
64
96
|
requirement: !ruby/object:Gem::Requirement
|
65
97
|
none: false
|
66
98
|
requirements:
|
@@ -76,7 +108,7 @@ dependencies:
|
|
76
108
|
- !ruby/object:Gem::Version
|
77
109
|
version: '0'
|
78
110
|
- !ruby/object:Gem::Dependency
|
79
|
-
name:
|
111
|
+
name: rake
|
80
112
|
requirement: !ruby/object:Gem::Requirement
|
81
113
|
none: false
|
82
114
|
requirements:
|
@@ -92,7 +124,7 @@ dependencies:
|
|
92
124
|
- !ruby/object:Gem::Version
|
93
125
|
version: '0'
|
94
126
|
- !ruby/object:Gem::Dependency
|
95
|
-
name:
|
127
|
+
name: rb-fsevent
|
96
128
|
requirement: !ruby/object:Gem::Requirement
|
97
129
|
none: false
|
98
130
|
requirements:
|
@@ -153,9 +185,17 @@ files:
|
|
153
185
|
- LICENSE.txt
|
154
186
|
- README.md
|
155
187
|
- Rakefile
|
188
|
+
- examples/finance/.rvmrc
|
189
|
+
- examples/finance/Gemfile
|
190
|
+
- examples/finance/app.rb
|
191
|
+
- examples/finance/database.yml
|
192
|
+
- examples/finance/domain.rb
|
193
|
+
- examples/finance/setup
|
156
194
|
- lib/rubiks.rb
|
157
195
|
- lib/rubiks/cube.rb
|
158
196
|
- lib/rubiks/dimension.rb
|
197
|
+
- lib/rubiks/hierarchy.rb
|
198
|
+
- lib/rubiks/transformers/lookup_transformer.rb
|
159
199
|
- lib/rubiks/version.rb
|
160
200
|
- rubiks.gemspec
|
161
201
|
- test/rubiks/test_cube.rb
|
@@ -173,18 +213,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
173
213
|
- - ! '>='
|
174
214
|
- !ruby/object:Gem::Version
|
175
215
|
version: '0'
|
176
|
-
segments:
|
177
|
-
- 0
|
178
|
-
hash: 3892697901423370992
|
179
216
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
180
217
|
none: false
|
181
218
|
requirements:
|
182
219
|
- - ! '>='
|
183
220
|
- !ruby/object:Gem::Version
|
184
221
|
version: '0'
|
185
|
-
segments:
|
186
|
-
- 0
|
187
|
-
hash: 3892697901423370992
|
188
222
|
requirements: []
|
189
223
|
rubyforge_project:
|
190
224
|
rubygems_version: 1.8.24
|