cell_set 0.1.0

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/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