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 CHANGED
@@ -2,6 +2,3 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in rubiks.gemspec
4
4
  gemspec
5
-
6
- gem 'yajl-ruby'
7
- gem 'redis'
@@ -0,0 +1 @@
1
+ rvm use jruby-1.7.0
@@ -0,0 +1,11 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rubiks', :path => '../../'
4
+
5
+ gem 'activerecord', :require => 'active_record'
6
+ gem 'activerecord-jdbcpostgresql-adapter'
7
+ gem 'awesome_print'
8
+ gem 'jruby-openssl'
9
+ gem 'mondrian-olap'
10
+ gem 'pry'
11
+ gem 'pry-nav'
@@ -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,5 @@
1
+ adapter: postgresql
2
+ encoding: utf8
3
+ host: localhost
4
+ username: postgres
5
+ database: finance_development
@@ -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
- class Cube
3
- def dimensions
2
+ module Cube
3
+ def self.included(klass)
4
+ klass.extend Rubiks::Cube::ClassMethods
4
5
  end
5
6
 
6
- def measures
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
@@ -1,6 +1,23 @@
1
1
  module Rubiks
2
- class Dimension
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,15 @@
1
+ module Rubiks
2
+ class Hierarchy
3
+ attr_accessor :name
4
+ attr_reader :levels
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ @levels = []
9
+ end
10
+
11
+ def level(name)
12
+ levels << name
13
+ end
14
+ end
15
+ 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
@@ -1,3 +1,3 @@
1
1
  module Rubiks
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
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 Cubes
8
- # autoload :Base, 'rubiks/cubes/base'
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 'rake'
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.1
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-10-18 00:00:00.000000000 Z
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: rake
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: rb-fsevent
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: guard
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: guard-minitest
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