cell_set 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,5 @@
1
+ cell_set is a ruby implementation of the CellSet and related models from mondrian.olap4j.
2
+
3
+ The cell_set gem automatically mixes in a to_ruby method on Mondrian::OLAP::CellSet that can be called from the instance variable @raw_cell_set in an instance of Mondrian::OLAP::Result.
4
+
5
+ Normally the to_ruby method is automatically called in the to_cell_set method in an instance of Mondrian::OLAP::Result.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,62 @@
1
+ # Use this module to add necessary attributes methods that ActiveModel::AttributeMethods doesn't add
2
+ # An ATTRIBUTES constant must be on the model before including this module
3
+ # Attributes specified in ATTRIBUTES will be serialized if any ActiveModel::Serializers are used
4
+ # Any attributes that shouldn't be serialized can still be set with another attr_accessor, but won't show up in
5
+ #
6
+
7
+ module CellSet
8
+ module Attributes
9
+ extend ActiveSupport::Concern
10
+ extend ActiveModel::Naming
11
+ include ActiveModel::AttributeMethods
12
+
13
+ included do
14
+ self.send(:attr_accessor, *self::ATTRIBUTES)
15
+
16
+ if self.private_methods.include?(:define_attribute_method)
17
+ self.send(:define_attribute_method, self::ATTRIBUTES)
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ def human_attribute_name(attr, options = {})
23
+ attr
24
+ end
25
+
26
+ def lookup_ancestors
27
+ [self]
28
+ end
29
+ end
30
+
31
+ def initialize(attrs = {})
32
+ @errors = ActiveModel::Errors.new(self)
33
+ attrs.each do |name, value|
34
+ send("#{name}=", value) if value
35
+ end
36
+ super(attrs)
37
+ end
38
+
39
+ def attributes
40
+ self.class::ATTRIBUTES.inject(ActiveSupport::HashWithIndifferentAccess.new) do |result, key|
41
+ result[key] = read_attribute_for_validation(key)
42
+ result
43
+ end
44
+ end
45
+
46
+ def attributes=(attrs)
47
+ attrs.each_pair {|k, v| send("#{k}=", v)}
48
+ end
49
+
50
+ def clear_attribute(attr)
51
+ send("#{attr}=", nil)
52
+ end
53
+
54
+ def has_attribute?(attr)
55
+ self.class::ATTRIBUTES.include?(attr)
56
+ end
57
+
58
+ def read_attribute_for_validation(key)
59
+ send(key)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,24 @@
1
+ module CellSet
2
+ class Cell
3
+ ATTRIBUTES = [:formatted_value, :ordinal, :value]
4
+
5
+ include Attributes
6
+ include ActiveModel::Serializers::JSON
7
+ include ActiveModel::Serializers::Xml
8
+ self.include_root_in_json = false
9
+
10
+ def from_json(*)
11
+ super.tap{|obj| obj.freeze}
12
+ end
13
+
14
+ def ordinal=(ordinal)
15
+ @ordinal = if ordinal.is_a?(Fixnum)
16
+ ordinal
17
+ elsif ordinal.is_a?(String)
18
+ Integer(ordinal)
19
+ else
20
+ throw ArgumentError
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,152 @@
1
+ require 'cell_set/cell_set_axis'
2
+ require 'cell_set/cell'
3
+
4
+ module CellSet
5
+ class CellSet
6
+ ATTRIBUTES = [:axes]
7
+
8
+ include Attributes
9
+ include ActiveModel::Serializers::JSON
10
+ include ActiveModel::Serializers::Xml
11
+
12
+ def as_json(options = nil)
13
+ # Strangely couldn't use options = {} in JRuby
14
+ options = {} if options.nil?
15
+ json = super(options)
16
+ unless (options.has_key?(:only) && !options[:only].include?("cells")) ||
17
+ (options.has_key?(:except) && options[:except].include?("cells"))
18
+ json.first[1]["cells"] = @cellHash
19
+ end
20
+ json
21
+ end
22
+
23
+ def axes=(axes)
24
+ if axes.is_a?(Array)
25
+ @axes = axes.map do |axis|
26
+ if axis.is_a?(CellSetAxis)
27
+ axis.cell_set = self
28
+ axis
29
+ elsif axis.is_a?(Hash)
30
+ CellSetAxis.new(:cell_set => self).from_json(axis.to_json)
31
+ else
32
+ throw ArgumentError
33
+ end
34
+ end
35
+ else
36
+ throw ArgumentError
37
+ end
38
+ end
39
+
40
+ def bounds
41
+ @axes.map{|axis| axis.positionCount}
42
+ end
43
+
44
+ def cell(*args)
45
+ if args[0].is_a?(NilClass)
46
+ throw(ArgumentError)
47
+ elsif args[0].is_a?(Fixnum) && args.length == 1
48
+ getCellInternal(args[0])
49
+ elsif args[0].is_a?(Array) && args.length == 1
50
+ getCellInternal(coordinatesToOrdinal(args[0]))
51
+ elsif args.select{|arg| arg.is_a?(Position)}.length == args.length
52
+ unless args.length == axes.length
53
+ throw ArgumentError, "cell coordinates should have dimension #{axis.length}"
54
+ end
55
+ cell(args.map{|position| position.getOrdinal})
56
+ else
57
+ throw(ArgumentError)
58
+ end
59
+ end
60
+
61
+ def cells=(cells)
62
+ if cells.is_a?(Array)
63
+ cells.each do |cell|
64
+ if cell.is_a?(Cell)
65
+ @cellHash[cell.ordinal.to_s] = {
66
+ "formatted_value" => cell.formatted_value,
67
+ "value" => cell.value
68
+ }
69
+ else
70
+ throw ArgumentError
71
+ end
72
+ end
73
+ elsif cells.is_a?(Hash)
74
+ @cellHash = cells
75
+ else
76
+ throw ArgumentError
77
+ end
78
+ end
79
+
80
+ def coordinatesToOrdinal(coordinates)
81
+ unless coordinates.length == @axes.length
82
+ throw ArgumentError, "Coordinates have different dimension #{coordinates.length} than axes #{@axes.length}"
83
+ end
84
+
85
+ modulo = 1
86
+ ordinal = 0
87
+ k = 0
88
+ @axes.each do |axis|
89
+ coordinate = coordinates[k]
90
+ if coordinate < 0 || coordinate >= axis.positionCount
91
+ throw IndexError, "Coordinate #{coordinate} of axis #{k} is out of range (#{bounds.join(", ")})"
92
+ end
93
+ ordinal += coordinate * modulo
94
+ modulo *= axis.positionCount
95
+ end
96
+ ordinal
97
+ end
98
+
99
+ def from_json(*)
100
+ super.tap{|obj| obj.freeze}
101
+ end
102
+
103
+ def getFilterAxis
104
+ throw NotImplementedError
105
+ end
106
+
107
+ def initialize(*)
108
+ @cellHash = {}
109
+ super
110
+ end
111
+
112
+ def maxOrdinal
113
+ modulo = 1
114
+ @axes.each do |axis|
115
+ modulo *= axis.positionCount
116
+ end
117
+ modulo
118
+ end
119
+
120
+ def ordinalToCoordinates(ordinal)
121
+ modulo = 1
122
+
123
+ list = @axes.map do |axis|
124
+ prevModulo = modulo
125
+ modulo *= axis.positionCount
126
+ (ordinal % modulo) / prevModulo
127
+ end
128
+
129
+ if ordinal < 0 || ordinal >= modulo
130
+ throw IndexError, "Cell ordinal #{ordinal} lies outside CellSet bounds (#{bounds.join(", ")})"
131
+ end
132
+
133
+ list
134
+ end
135
+
136
+ private
137
+ attr_accessor :cellHash
138
+
139
+ def getCellInternal(pos)
140
+ cell = @cellHash[pos.to_s]
141
+ if cell.nil?
142
+ if (pos < 0 || pos >= maxOrdinal)
143
+ throw IndexError
144
+ else
145
+ Cell.new(:formatted_value => "", :ordinal => pos)
146
+ end
147
+ else
148
+ Cell.new(:formatted_value => cell["formatted_value"], :ordinal => pos, :value => cell["value"])
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,79 @@
1
+ require 'cell_set/position'
2
+
3
+ module CellSet
4
+ class CellSetAxis
5
+ ATTRIBUTES = [:axis_ordinal, :cell_set, :positions]
6
+
7
+ include Attributes
8
+ include ActiveModel::Serializers::JSON
9
+ include ActiveModel::Serializers::Xml
10
+ include Comparable
11
+ self.include_root_in_json = false
12
+
13
+ def <=>(other)
14
+ if other.is_a?(self.class)
15
+ @axis_ordinal <=> other.axis_ordinal
16
+ else
17
+ raise(ArgumentError, "Must compare #{self.class.to_s} to another #{self.class.to_s}")
18
+ end
19
+ end
20
+
21
+ def axis_ordinal=(ordinal)
22
+ @axis_ordinal = if ordinal.is_a?(Fixnum)
23
+ ordinal
24
+ elsif ordinal.is_a?(String)
25
+ Integer(ordinal)
26
+ else
27
+ throw ArgumentError
28
+ end
29
+ end
30
+
31
+ def between?(min, max)
32
+ @axis_ordinal <= min || @axis_ordinal >= max
33
+ end
34
+
35
+ def cell_set=(cell_set)
36
+ @cell_set = if cell_set.is_a?(CellSet)
37
+ cell_set
38
+ elsif cell_set.is_a?(Hash)
39
+ CellSet.new.from_json(cell_set.to_json)
40
+ else
41
+ throw ArgumentError
42
+ end
43
+ end
44
+
45
+ def from_json(*)
46
+ super.tap{|obj| obj.freeze}
47
+ end
48
+
49
+ def positionCount
50
+ @positions.length
51
+ end
52
+
53
+ def positions=(positions)
54
+ if positions.is_a?(Array)
55
+ @positions = positions.map do |position|
56
+ if position.is_a?(Position)
57
+ position
58
+ elsif position.is_a?(Hash)
59
+ Position.new.from_json(position.to_json)
60
+ else
61
+ throw ArgumentError
62
+ end
63
+ end
64
+ else
65
+ throw ArgumentError
66
+ end
67
+ end
68
+
69
+ def as_json(options = nil)
70
+ # Strangely couldn't use options = {} in JRuby
71
+ options = {} if options.nil?
72
+ unless (options.has_key?(:only) && options[:only].include?("cell_set")) ||
73
+ (options.has_key?(:except) && options[:except].include?("cell_set"))
74
+ options[:except] = (options[:except] || []).push("cell_set")
75
+ end
76
+ super(options)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,75 @@
1
+ module CellSet
2
+ class Member
3
+ ATTRIBUTES = [:caption, :childMembers, :depth, :description, :dimension, :expression, :hierarchy, :level, :memberType, \
4
+ :name, :ordinal, :parentMember, :uniqueName]
5
+
6
+ include Attributes
7
+ include ActiveModel::Serializers::JSON
8
+ include ActiveModel::Serializers::Xml
9
+ include Comparable
10
+ self.include_root_in_json = false
11
+
12
+ def <=>(other)
13
+ if other.is_a?(self.class)
14
+ @ordinal <=> other.ordinal
15
+ else
16
+ nil
17
+ end
18
+ end
19
+
20
+ def ancestorMembers
21
+ throw NotImplementedError
22
+ end
23
+
24
+ def between?(min, max)
25
+ @ordinal <= min || @ordinal >= max
26
+ end
27
+
28
+ def childMemberCount
29
+ throw NotImplementedError
30
+ end
31
+
32
+ def all?
33
+ throw NotImplementedError
34
+ end
35
+
36
+ def calculated?
37
+ throw NotImplementedError
38
+ end
39
+
40
+ def calculatedInQuery?
41
+ throw NotImplementedError
42
+ end
43
+
44
+ def childOrEqualTo?(member)
45
+ throw NotImplementedError
46
+ end
47
+
48
+ def from_json(*)
49
+ super.tap{|obj| obj.freeze}
50
+ end
51
+
52
+ def hidden?
53
+ throw NotImplementedError
54
+ end
55
+
56
+ def initialize(*)
57
+ @childMembers = []
58
+ super
59
+ end
60
+
61
+ def ordinal=(ordinal)
62
+ @ordinal = if ordinal.is_a?(Fixnum)
63
+ ordinal
64
+ elsif ordinal.is_a?(String)
65
+ Integer(ordinal)
66
+ else
67
+ throw ArgumentError
68
+ end
69
+ end
70
+
71
+ def visible?
72
+ throw NotImplementedError
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,5 @@
1
+ module CellSet::Model
2
+ extend ActiveSupport::Concern
3
+
4
+ ActiveSupport.run_load_hooks(:cell_set_model, self)
5
+ end
@@ -0,0 +1,60 @@
1
+ require 'cell_set/member'
2
+
3
+ module CellSet
4
+ class Position
5
+ ATTRIBUTES = [:members, :ordinal]
6
+
7
+ include Attributes
8
+ include ActiveModel::Serializers::JSON
9
+ include ActiveModel::Serializers::Xml
10
+ include Comparable
11
+ self.include_root_in_json = false
12
+
13
+ def <=>(other)
14
+ if other.is_a?(self.class)
15
+ @ordinal <=> other.ordinal
16
+ else
17
+ nil
18
+ end
19
+ end
20
+
21
+ def between?(min, max)
22
+ @ordinal <= min || @ordinal >= max
23
+ end
24
+
25
+ def from_json(*)
26
+ super.tap{|obj| obj.freeze}
27
+ end
28
+
29
+ def initialize(*)
30
+ @members = []
31
+ super
32
+ end
33
+
34
+ def members=(members)
35
+ if members.is_a?(Array)
36
+ @members = members.map do |member|
37
+ if member.is_a?(Member)
38
+ member
39
+ elsif member.is_a?(Hash)
40
+ Member.new.from_json(member.to_json)
41
+ else
42
+ throw ArgumentError
43
+ end
44
+ end
45
+ else
46
+ throw ArgumentError
47
+ end
48
+ end
49
+
50
+ def ordinal=(ordinal)
51
+ @ordinal = if ordinal.is_a?(Fixnum)
52
+ ordinal
53
+ elsif ordinal.is_a?(String)
54
+ Integer(ordinal)
55
+ else
56
+ throw ArgumentError
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,9 @@
1
+ module CellSet
2
+ module Result
3
+ extend ActiveSupport::Concern
4
+
5
+ def to_cell_set
6
+ @raw_cell_set.to_ruby
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Version
2
+ VERSION = "0.1.0"
3
+ end
data/lib/cell_set.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/hash_with_indifferent_access'
3
+ require 'active_model/attribute_methods'
4
+ require 'active_model/naming'
5
+ require 'active_model/serialization'
6
+ require 'active_model/serializers/json'
7
+ require 'active_model/serializers/xml'
8
+ if RUBY_PLATFORM == "java"
9
+ require 'mondrian-olap'
10
+ end
11
+
12
+ %w(version attributes result cell member position cell_set_axis cell_set model).each do |file|
13
+ require "cell_set/#{file}"
14
+ end
15
+
16
+ %w(ruby).each do |file|
17
+ require "mondrian/olap/cell_set/#{file}"
18
+ end
19
+
20
+ if RUBY_PLATFORM == "java"
21
+ ActiveSupport.on_load(:cell_set_model) do
22
+ Java::Mondrian::olap4j::MondrianOlap4jCellSet.send(:include, Mondrian::OLAP::CellSet::Ruby)
23
+ Mondrian::OLAP::Result.send(:include, ::CellSet::Result)
24
+ end
25
+ end
@@ -0,0 +1,61 @@
1
+ module Mondrian
2
+ module OLAP
3
+ module CellSet
4
+ module Ruby
5
+ extend ActiveSupport::Concern
6
+
7
+ def to_ruby
8
+ cell_set = ::CellSet::CellSet.new
9
+ axes = getAxes.map do |axis|
10
+ ::CellSet::CellSetAxis.new(:axis_ordinal => axis.getAxisOrdinal.axisOrdinal, :cell_set => cell_set, :positions => buildPositions(axis))
11
+ end
12
+ cell_set.axes = axes
13
+ cell_set.send("cellHash=", getCellHash)
14
+ cell_set.freeze
15
+ cell_set
16
+ end
17
+
18
+ private
19
+ def buildPositions(axis)
20
+ axis.positions.map do |position|
21
+ members = position.getMembers.map do |member|
22
+ ::CellSet::Member.new(
23
+ :caption => member.getCaption,
24
+ :depth => member.getDepth,
25
+ :description => member.getDescription,
26
+ :memberType => member.getMemberType.to_s,
27
+ :name => member.getName,
28
+ :ordinal => member.getOrdinal,
29
+ :uniqueName => member.getUniqueName
30
+ )
31
+ end
32
+ ::CellSet::Position.new(:members => members, :ordinal => position.getOrdinal)
33
+ end
34
+ end
35
+
36
+ def getCellHash
37
+ cells = {}
38
+ maxOrdinal.times do |i|
39
+ formatted_value, value = *getCell(i).try{|cell| [cell.getFormattedValue, cell.getValue]}
40
+ unless formatted_value.blank? && value.nil?
41
+ # Build key as string
42
+ cells[i.to_s] = {
43
+ "formatted_value" => formatted_value,
44
+ "value" => value
45
+ }
46
+ end
47
+ end
48
+ cells
49
+ end
50
+
51
+ def maxOrdinal
52
+ modulo = 1
53
+ getAxes.each do |axis|
54
+ modulo *= axis.getPositionCount
55
+ end
56
+ modulo
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,4 @@
1
+ Factory.define :cell_set_axis, :class => CellSet::CellSetAxis do |f|
2
+ f.axis_ordinal 0
3
+ f.positions []
4
+ end
@@ -0,0 +1,3 @@
1
+ Factory.define :cell_set, :class => CellSet::CellSet do |f|
2
+ f.axes []
3
+ end
@@ -0,0 +1,5 @@
1
+ Factory.define :member, :class => CellSet::Member do |f|
2
+ f.ordinal 0
3
+ f.name { Factory.next(:name) }
4
+ f.uniqueName { "[All].[#{name}]" }
5
+ end
@@ -0,0 +1,4 @@
1
+ Factory.define :position, :class => CellSet::Position do |f|
2
+ f.ordinal 0
3
+ f.members []
4
+ end
@@ -0,0 +1,7 @@
1
+ Factory.sequence :id do |n|
2
+ n
3
+ end
4
+
5
+ Factory.sequence :name do |n|
6
+ "String#{n}Name"
7
+ end