rubiks 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,4 +1,19 @@
|
|
1
|
-
source '
|
1
|
+
source 'http://rubygems.org'
|
2
2
|
|
3
|
-
#
|
3
|
+
# rubiks.gemspec defines the runtime dependencies
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
# Test and development dependencies are defined here
|
7
|
+
# so CI can include just test dependencies
|
8
|
+
group :test do
|
9
|
+
gem 'rake'
|
10
|
+
gem 'rspec'
|
11
|
+
gem 'rspec-pride'
|
12
|
+
end
|
13
|
+
|
14
|
+
group :development do
|
15
|
+
gem 'awesome_print'
|
16
|
+
gem 'pry'
|
17
|
+
gem 'pry-nav'
|
18
|
+
gem 'simplecov'
|
19
|
+
end
|
data/Guardfile
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
guard '
|
2
|
-
watch(%r
|
3
|
-
watch(%r
|
4
|
-
watch(
|
1
|
+
guard 'rspec' do
|
2
|
+
watch(%r{^spec/.+_spec\.rb$})
|
3
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
4
|
+
watch('spec/spec_helper.rb') { "spec" }
|
5
5
|
end
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
# Rubiks
|
2
2
|
|
3
|
-
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/rubiks.png)](http://badge.fury.io/rb/rubiks)
|
4
|
+
[![Build Status](https://secure.travis-ci.org/moneydesktop/rubiks.png?branch=master)](https://travis-ci.org/moneydesktop/rubiks)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/moneydesktop/rubiks.png)](https://codeclimate.com/github/moneydesktop/rubiks)
|
4
6
|
|
5
|
-
|
7
|
+
Rubiks is a Ruby gem that defines an OLAP schema with `#to_xml` to generate a Mondrian XML schema and `#to_json` for everything else.
|
6
8
|
|
7
|
-
|
9
|
+
## Installation
|
8
10
|
|
9
|
-
|
11
|
+
Run `gem install rubiks` to install the gem on its own.
|
12
|
+
|
13
|
+
Or you can add the following to your Gemfile and run the `bundle` command to install it.
|
10
14
|
|
15
|
+
gem 'rubiks'
|
11
16
|
|
12
17
|
## Contributing
|
13
18
|
|
data/Rakefile
CHANGED
@@ -1,10 +1,5 @@
|
|
1
|
-
require '
|
2
|
-
require 'rake'
|
3
|
-
require 'rake/testtask'
|
1
|
+
require 'rspec/core/rake_task'
|
4
2
|
|
5
|
-
|
6
|
-
t.pattern = 'test/**/test_*.rb'
|
7
|
-
t.libs.push 'test'
|
8
|
-
end
|
3
|
+
RSpec::Core::RakeTask.new(:spec)
|
9
4
|
|
10
|
-
task :default => :
|
5
|
+
task :default => :spec
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubiks/nodes/validated_node'
|
2
|
+
|
3
|
+
module ::Rubiks
|
4
|
+
class AnnotatedNode < ::Rubiks::ValidatedNode
|
5
|
+
value :name, String
|
6
|
+
|
7
|
+
validates :name_present
|
8
|
+
|
9
|
+
def name_present
|
10
|
+
errors << "Name required on #{self.class.name.split('::').last}" if self.name.blank?
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_name(name_value)
|
14
|
+
return if name_value.nil?
|
15
|
+
|
16
|
+
self.name = name_value.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'rubiks/nodes/annotated_node'
|
2
|
+
require 'rubiks/nodes/dimension'
|
3
|
+
require 'rubiks/nodes/measure'
|
4
|
+
|
5
|
+
module ::Rubiks
|
6
|
+
|
7
|
+
class Cube < ::Rubiks::AnnotatedNode
|
8
|
+
child :dimensions, [::Rubiks::Dimension]
|
9
|
+
child :measures, [::Rubiks::Measure]
|
10
|
+
|
11
|
+
validates :dimensions_present, :measures_present
|
12
|
+
|
13
|
+
def self.new_from_hash(hash={})
|
14
|
+
new_instance = new('',[],[])
|
15
|
+
return new_instance.from_hash(hash)
|
16
|
+
end
|
17
|
+
|
18
|
+
def from_hash(working_hash)
|
19
|
+
return self if working_hash.nil?
|
20
|
+
working_hash.stringify_keys!
|
21
|
+
|
22
|
+
parse_name(working_hash.delete('name'))
|
23
|
+
parse_dimensions(working_hash.delete('dimensions'))
|
24
|
+
parse_measures(working_hash.delete('measures'))
|
25
|
+
return self
|
26
|
+
end
|
27
|
+
|
28
|
+
def measures_present
|
29
|
+
if self.measures.present?
|
30
|
+
self.measures.each do |measure|
|
31
|
+
measure.validate
|
32
|
+
errors.push(*measure.errors)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
errors << 'Measures Required for Cube'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_measures(measures_array)
|
40
|
+
return if measures_array.nil? || measures_array.empty?
|
41
|
+
|
42
|
+
measures_array.each do |measure_hash|
|
43
|
+
self.measures << ::Rubiks::Measure.new_from_hash(measure_hash)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def dimensions_present
|
48
|
+
if self.dimensions.present?
|
49
|
+
self.dimensions.each do |dimension|
|
50
|
+
dimension.validate
|
51
|
+
errors.push(*dimension.errors)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
errors << 'Dimensions Required for Cube'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_dimensions(dimensions_array)
|
59
|
+
return if dimensions_array.nil? || dimensions_array.empty?
|
60
|
+
|
61
|
+
dimensions_array.each do |dimension_hash|
|
62
|
+
self.dimensions << ::Rubiks::Dimension.new_from_hash(dimension_hash)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_hash
|
67
|
+
hash = {}
|
68
|
+
|
69
|
+
hash['name'] = self.name.to_s if self.name.present?
|
70
|
+
hash['dimensions'] = self.dimensions.map(&:to_hash) if self.dimensions.present?
|
71
|
+
hash['measures'] = self.measures.map(&:to_hash) if self.measures.present?
|
72
|
+
|
73
|
+
return hash
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubiks/nodes/validated_node'
|
2
|
+
require 'rubiks/nodes/hierarchy'
|
3
|
+
|
4
|
+
module ::Rubiks
|
5
|
+
|
6
|
+
class Dimension < ::Rubiks::AnnotatedNode
|
7
|
+
child :hierarchies, [::Rubiks::Hierarchy]
|
8
|
+
|
9
|
+
validates :hierarchies_present
|
10
|
+
|
11
|
+
def self.new_from_hash(hash={})
|
12
|
+
new_instance = new('',[])
|
13
|
+
return new_instance.from_hash(hash)
|
14
|
+
end
|
15
|
+
|
16
|
+
def hierarchies_present
|
17
|
+
if self.hierarchies.present?
|
18
|
+
self.hierarchies.each do |hierarchy|
|
19
|
+
hierarchy.validate
|
20
|
+
errors.push(*hierarchy.errors)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
errors << 'Hierarchies Required for Dimension'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def from_hash(working_hash)
|
28
|
+
return self if working_hash.nil?
|
29
|
+
working_hash.stringify_keys!
|
30
|
+
|
31
|
+
parse_name(working_hash.delete('name'))
|
32
|
+
parse_hierarchies(working_hash.delete('hierarchies'))
|
33
|
+
return self
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_hierarchies(hierarchies_array)
|
37
|
+
return if hierarchies_array.nil? || hierarchies_array.empty?
|
38
|
+
|
39
|
+
hierarchies_array.each do |hierarchy_hash|
|
40
|
+
self.hierarchies << ::Rubiks::Hierarchy.new_from_hash(hierarchy_hash)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_hash
|
45
|
+
hash = {}
|
46
|
+
|
47
|
+
hash['name'] = self.name.to_s if self.name.present?
|
48
|
+
hash['hierarchies'] = self.hierarchies.map(&:to_hash) if self.hierarchies.present?
|
49
|
+
|
50
|
+
return hash
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubiks/nodes/annotated_node'
|
2
|
+
require 'rubiks/nodes/level'
|
3
|
+
|
4
|
+
module ::Rubiks
|
5
|
+
|
6
|
+
class Hierarchy < ::Rubiks::AnnotatedNode
|
7
|
+
child :levels, [::Rubiks::Level]
|
8
|
+
|
9
|
+
validates :levels_present
|
10
|
+
|
11
|
+
def self.new_from_hash(hash={})
|
12
|
+
new_instance = new('',[])
|
13
|
+
return new_instance.from_hash(hash)
|
14
|
+
end
|
15
|
+
|
16
|
+
def levels_present
|
17
|
+
if self.levels.present?
|
18
|
+
self.levels.each do |level|
|
19
|
+
level.validate
|
20
|
+
errors.push(*level.errors)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
errors << 'Levels Required for Hierarchy'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def from_hash(working_hash)
|
28
|
+
return self if working_hash.nil?
|
29
|
+
working_hash.stringify_keys!
|
30
|
+
|
31
|
+
parse_name(working_hash.delete('name'))
|
32
|
+
parse_levels(working_hash.delete('levels'))
|
33
|
+
return self
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_levels(levels_array)
|
37
|
+
return if levels_array.nil? || levels_array.empty?
|
38
|
+
|
39
|
+
levels_array.each do |level_hash|
|
40
|
+
self.levels << ::Rubiks::Level.new_from_hash(level_hash)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_hash
|
45
|
+
hash = {}
|
46
|
+
|
47
|
+
hash['name'] = self.name.to_s if self.name.present?
|
48
|
+
hash['levels'] = self.levels.map(&:to_hash) if self.levels.present?
|
49
|
+
|
50
|
+
return hash
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rubiks/nodes/annotated_node'
|
2
|
+
require 'rubiks/nodes/hierarchy'
|
3
|
+
|
4
|
+
module ::Rubiks
|
5
|
+
|
6
|
+
class Level < ::Rubiks::AnnotatedNode
|
7
|
+
def self.new_from_hash(hash={})
|
8
|
+
new_instance = new
|
9
|
+
return new_instance.from_hash(hash)
|
10
|
+
end
|
11
|
+
|
12
|
+
def from_hash(working_hash)
|
13
|
+
return self if working_hash.nil?
|
14
|
+
working_hash.stringify_keys!
|
15
|
+
|
16
|
+
parse_name(working_hash.delete('name'))
|
17
|
+
return self
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash
|
21
|
+
hash = {}
|
22
|
+
|
23
|
+
hash['name'] = self.name.to_s if self.name.present?
|
24
|
+
|
25
|
+
return hash
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'rubiks/nodes/validated_node'
|
2
|
+
|
3
|
+
module ::Rubiks
|
4
|
+
|
5
|
+
class Measure < ::Rubiks::AnnotatedNode
|
6
|
+
value :column, String
|
7
|
+
value :aggregator, String
|
8
|
+
value :format_string, String
|
9
|
+
|
10
|
+
validates :column_present, :aggregator_present
|
11
|
+
|
12
|
+
def self.new_from_hash(hash={})
|
13
|
+
new_instance = new
|
14
|
+
return new_instance.from_hash(hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
def from_hash(working_hash)
|
18
|
+
return self if working_hash.nil?
|
19
|
+
working_hash.stringify_keys!
|
20
|
+
|
21
|
+
parse_name(working_hash.delete('name'))
|
22
|
+
parse_column(working_hash.delete('column'))
|
23
|
+
parse_aggregator(working_hash.delete('aggregator'))
|
24
|
+
parse_format_string(working_hash.delete('format_string'))
|
25
|
+
return self
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_hash
|
29
|
+
hash = {}
|
30
|
+
|
31
|
+
hash['name'] = self.name.to_s if self.name.present?
|
32
|
+
hash['column'] = self.column.to_s if self.column.present?
|
33
|
+
hash['aggregator'] = self.aggregator.to_s if self.aggregator.present?
|
34
|
+
hash['format_string'] = self.format_string.to_s if self.format_string.present?
|
35
|
+
|
36
|
+
return hash
|
37
|
+
end
|
38
|
+
|
39
|
+
def column_present
|
40
|
+
errors << 'Column required on Measure' if self.column.blank?
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_column(column_value)
|
44
|
+
return if column_value.nil?
|
45
|
+
|
46
|
+
self.column = column_value.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def aggregator_present
|
50
|
+
errors << 'Aggregator required on Measure' if self.aggregator.blank?
|
51
|
+
end
|
52
|
+
|
53
|
+
def parse_aggregator(aggregator_value)
|
54
|
+
return if aggregator_value.nil?
|
55
|
+
|
56
|
+
self.aggregator = aggregator_value.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_format_string(format_string_value)
|
60
|
+
return if format_string_value.nil?
|
61
|
+
|
62
|
+
self.format_string = format_string_value.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rubiks/nodes/validated_node'
|
2
|
+
require 'rubiks/nodes/cube'
|
3
|
+
require 'multi_json'
|
4
|
+
|
5
|
+
module ::Rubiks
|
6
|
+
|
7
|
+
class Schema < ::Rubiks::ValidatedNode
|
8
|
+
child :cubes, [::Rubiks::Cube]
|
9
|
+
|
10
|
+
validates :cubes_present
|
11
|
+
|
12
|
+
def self.new_from_hash(hash={})
|
13
|
+
new_instance = new([])
|
14
|
+
return new_instance.from_hash(hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
def cubes_present
|
18
|
+
if self.cubes.present?
|
19
|
+
self.cubes.each do |cube|
|
20
|
+
cube.validate
|
21
|
+
errors.push(*cube.errors)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
errors << 'Cubes Required for Schema'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def from_hash(working_hash)
|
29
|
+
return self if working_hash.nil?
|
30
|
+
working_hash.stringify_keys!
|
31
|
+
|
32
|
+
parse_cubes(working_hash.delete('cubes'))
|
33
|
+
return self
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_cubes(cubes_array)
|
37
|
+
return if cubes_array.nil? || cubes_array.empty?
|
38
|
+
|
39
|
+
cubes_array.each do |cube_hash|
|
40
|
+
self.cubes << ::Rubiks::Cube.new_from_hash(cube_hash)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_hash
|
45
|
+
hash = {}
|
46
|
+
|
47
|
+
hash['cubes'] = self.cubes.map(&:to_hash) if self.cubes.present?
|
48
|
+
|
49
|
+
return hash
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_json
|
53
|
+
MultiJson.dump(to_hash)
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_xml
|
57
|
+
to_hash.to_xml(:root => 'Schema')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rltk'
|
2
|
+
require 'rltk/ast'
|
3
|
+
|
4
|
+
module ::Rubiks
|
5
|
+
class ValidatedNode < ::RLTK::ASTNode
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :validators
|
9
|
+
|
10
|
+
alias_method :validator_methods, :validators
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.validates(*validator_symbols)
|
14
|
+
@validators ||= []
|
15
|
+
@validators << validator_symbols.flatten
|
16
|
+
@validators.flatten!
|
17
|
+
@validators.compact!
|
18
|
+
@validators.uniq!
|
19
|
+
|
20
|
+
return @validators
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.inherited(klass)
|
24
|
+
super
|
25
|
+
klass.validators = self.validators.nil? ?
|
26
|
+
[] :
|
27
|
+
self.validators.dup
|
28
|
+
end
|
29
|
+
|
30
|
+
def errors
|
31
|
+
@errors ||= []
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid?
|
35
|
+
validate if errors.empty?
|
36
|
+
|
37
|
+
return errors.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate
|
41
|
+
self.class.validator_methods.each do |validator_method|
|
42
|
+
self.__send__(validator_method) if respond_to?(validator_method)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/rubiks/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module Rubiks
|
2
|
-
VERSION = '0.0.
|
1
|
+
module ::Rubiks
|
2
|
+
VERSION = '0.0.4'
|
3
3
|
end
|
data/lib/rubiks.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'rubiks/version'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
require 'active_support/core_ext/hash/deep_dup'
|
5
|
+
require 'active_support/core_ext/hash/keys'
|
6
|
+
require 'active_support/core_ext/hash/conversions'
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
end
|
8
|
+
nodes_directory = File.expand_path('../rubiks/nodes', __FILE__)
|
9
|
+
Dir["#{nodes_directory}/*.rb"].each { |file| require file }
|
data/rubiks.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.version = Rubiks::VERSION
|
9
9
|
gem.authors = ['JohnnyT']
|
10
10
|
gem.email = ['johnnyt@moneydesktop.com']
|
11
|
-
gem.description = %q{
|
12
|
-
gem.summary = gem.
|
11
|
+
gem.description = %q{Define an OLAP schema}
|
12
|
+
gem.summary = 'Rubiks is a Ruby gem that defines an OLAP schema and can output the schema as XML and JSON.'
|
13
13
|
gem.homepage = 'https://github.com/moneydesktop/rubiks'
|
14
14
|
|
15
15
|
gem.files = `git ls-files`.split($/)
|
@@ -17,15 +17,7 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ['lib']
|
19
19
|
|
20
|
-
gem.add_dependency '
|
21
|
-
|
22
|
-
gem.
|
23
|
-
gem.add_development_dependency 'guard'
|
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'
|
29
|
-
gem.add_development_dependency 'simplecov'
|
30
|
-
gem.add_development_dependency 'simplecov-gem-adapter'
|
20
|
+
gem.add_dependency 'rltk'
|
21
|
+
gem.add_dependency 'activesupport'
|
22
|
+
gem.add_dependency 'builder'
|
31
23
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# This example is taken from the Mondrian documentation:
|
4
|
+
# http://mondrian.pentaho.com/documentation/schema.php#Cubes_and_dimensions
|
5
|
+
# and then modified to use Rails/Rubiks conventions
|
6
|
+
#
|
7
|
+
# We want the output to be:
|
8
|
+
#
|
9
|
+
# <Schema>
|
10
|
+
# <Cube name="Sales">
|
11
|
+
# <Table name="view_sales"/>
|
12
|
+
#
|
13
|
+
# <Dimension name="Gender" foreignKey="customer_id">
|
14
|
+
# <Hierarchy hasAll="true" allMemberName="All Genders" primaryKey="id">
|
15
|
+
# <Table name="customers"/>
|
16
|
+
# <Level name="Gender" column="gender" uniqueMembers="true"/>
|
17
|
+
# </Hierarchy>
|
18
|
+
# </Dimension>
|
19
|
+
#
|
20
|
+
# <Dimension name="Date" foreignKey="date_id">
|
21
|
+
# <Hierarchy hasAll="false" primaryKey="id">
|
22
|
+
# <Table name="view_dates"/>
|
23
|
+
# <Level name="Year" column="year" type="Numeric" uniqueMembers="true"/>
|
24
|
+
# <Level name="Quarter" column="quarter" uniqueMembers="false"/>
|
25
|
+
# <Level name="Month" column="month_of_year" type="Numeric" uniqueMembers="false"/>
|
26
|
+
# </Hierarchy>
|
27
|
+
# </Dimension>
|
28
|
+
#
|
29
|
+
# <Measure name="Unit Sales" column="unit_sales" aggregator="sum" formatString="#,###"/>
|
30
|
+
# <Measure name="Store Sales" column="store_sales" aggregator="sum" formatString="#,###.##"/>
|
31
|
+
# <Measure name="Store Cost" column="store_cost" aggregator="sum" formatString="#,###.00"/>
|
32
|
+
#
|
33
|
+
# <CalculatedMember name="Profit" dimension="Measures" formula="[Measures].[Store Sales] - [Measures].[Store Cost]">
|
34
|
+
# <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>
|
35
|
+
# </CalculatedMember>
|
36
|
+
# </Cube>
|
37
|
+
# </Schema>
|
38
|
+
|
39
|
+
describe 'A basic Mondrian XML Schema' do
|
40
|
+
let(:described_class) { ::Rubiks::Schema }
|
41
|
+
let(:schema_hash) {
|
42
|
+
{
|
43
|
+
'cubes' => [{
|
44
|
+
'name' => 'sales',
|
45
|
+
'measures' => [
|
46
|
+
{
|
47
|
+
'name' => 'unit_sales',
|
48
|
+
'aggregator' => 'sum',
|
49
|
+
'format_string' => '#,###'
|
50
|
+
},
|
51
|
+
{
|
52
|
+
'name' => 'store_sales',
|
53
|
+
'aggregator' => 'sum',
|
54
|
+
'format_string' => '#,###.##'
|
55
|
+
},
|
56
|
+
{
|
57
|
+
'name' => 'store_cost',
|
58
|
+
'aggregator' => 'sum',
|
59
|
+
'format_string' => '#,###.00'
|
60
|
+
}
|
61
|
+
],
|
62
|
+
'dimensions' => [
|
63
|
+
{
|
64
|
+
'name' => 'date',
|
65
|
+
'hierarchies' => [{
|
66
|
+
'name' => 'day_of_week',
|
67
|
+
'levels' => [{
|
68
|
+
'name' => 'day_of_week'
|
69
|
+
}]
|
70
|
+
}]
|
71
|
+
}
|
72
|
+
]
|
73
|
+
}]
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
# subject { described_class.new_from_hash(schema_hash) }
|
78
|
+
|
79
|
+
# describe '#to_json' do
|
80
|
+
# it 'generates a JSON string' do
|
81
|
+
# subject.to_json.should be_kind_of String
|
82
|
+
# end
|
83
|
+
|
84
|
+
# it 'generates a valid JSON string' do
|
85
|
+
# lambda {
|
86
|
+
# MultiJson.load(subject.to_json)
|
87
|
+
# }.should_not raise_error MultiJson::LoadError
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# describe ::Rubiks::AnnotatedNode do
|
4
|
+
# # API
|
5
|
+
# specify { subject.respond_to?(:from_hash) }
|
6
|
+
# specify { subject.respond_to?(:to_hash) }
|
7
|
+
#
|
8
|
+
# context 'when parsed from a valid hash' do
|
9
|
+
# subject { described_class.new.from_hash('name' => 'some_name') }
|
10
|
+
#
|
11
|
+
# its(:name) { should eq('some_name') }
|
12
|
+
#
|
13
|
+
# its(:to_hash) { should have_key('name') }
|
14
|
+
#
|
15
|
+
# it { should be_valid }
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# context 'when parsed from an invalid (empty) hash' do
|
19
|
+
# subject { described_class.new.from_hash({}) }
|
20
|
+
#
|
21
|
+
# it { should_not be_valid }
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# end
|