cube 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+
9
+ gem "savon"
10
+ gem "webmock"
11
+
12
+ group :development do
13
+ gem "vcr"
14
+ gem "rspec", "~> 2.3.0"
15
+ gem "bundler", "~> 1.0.0"
16
+ gem "jeweler", "~> 1.6.4"
17
+ end
@@ -0,0 +1,55 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ addressable (2.2.6)
5
+ akami (1.0.0)
6
+ gyoku (>= 0.4.0)
7
+ builder (3.0.0)
8
+ crack (0.3.1)
9
+ diff-lcs (1.1.3)
10
+ git (1.2.5)
11
+ gyoku (0.4.4)
12
+ builder (>= 2.1.2)
13
+ httpi (0.9.5)
14
+ rack
15
+ jeweler (1.6.4)
16
+ bundler (~> 1.0)
17
+ git (>= 1.2.5)
18
+ rake
19
+ nokogiri (1.5.0)
20
+ nori (1.0.2)
21
+ rack (1.4.1)
22
+ rake (0.9.2.2)
23
+ rspec (2.3.0)
24
+ rspec-core (~> 2.3.0)
25
+ rspec-expectations (~> 2.3.0)
26
+ rspec-mocks (~> 2.3.0)
27
+ rspec-core (2.3.1)
28
+ rspec-expectations (2.3.0)
29
+ diff-lcs (~> 1.1.2)
30
+ rspec-mocks (2.3.0)
31
+ savon (0.9.7)
32
+ akami (~> 1.0)
33
+ builder (>= 2.1.2)
34
+ gyoku (>= 0.4.0)
35
+ httpi (~> 0.9)
36
+ nokogiri (>= 1.4.0)
37
+ nori (~> 1.0)
38
+ wasabi (~> 2.0)
39
+ vcr (1.11.3)
40
+ wasabi (2.0.0)
41
+ nokogiri (>= 1.4.0)
42
+ webmock (1.7.10)
43
+ addressable (~> 2.2, > 2.2.5)
44
+ crack (>= 0.1.7)
45
+
46
+ PLATFORMS
47
+ ruby
48
+
49
+ DEPENDENCIES
50
+ bundler (~> 1.0.0)
51
+ jeweler (~> 1.6.4)
52
+ rspec (~> 2.3.0)
53
+ savon
54
+ vcr
55
+ webmock
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 drKreso
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,54 @@
1
+ Cube
2
+ ================
3
+
4
+ Use this gem talk to the OLAP based backend via Xmla SOAP messages.
5
+ You can send (simple) MDX queries and get the result back in a human friendly from.
6
+
7
+ Installation
8
+ ------------
9
+ Add to Gemfile
10
+
11
+ ```
12
+ gem 'cube'
13
+ ```
14
+
15
+ Configuration
16
+ --------------
17
+ Set up your catalog and endpoint
18
+
19
+ ```
20
+ Xmla.configure do |c|
21
+ c.endpoint = "http://localhost:8282/icCube/xmla"
22
+ c.catalog = "GOSJAR"
23
+ end
24
+ ```
25
+
26
+ Usage
27
+ -------
28
+ ```
29
+ table = Cube.execute("select [Location].[City].children on COLUMNS, [Measures].[Count] on ROWS from [GOSJAR]")
30
+ ```
31
+
32
+ Limitations
33
+ ------------
34
+ * No drill down (fails to even parse the result)
35
+ * No multi named columns
36
+ * Tested only with icCube, in theory works with every Xmla provider
37
+
38
+ Contributing to cube
39
+ -------------------------------
40
+
41
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
42
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
43
+ * Fork the project
44
+ * Start a feature/bugfix branch
45
+ * Commit and push until you are happy with your contribution
46
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
47
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
48
+
49
+ Copyright
50
+ ----------
51
+
52
+ Copyright (c) 2012 drKreso. See LICENSE.txt for
53
+ further details.
54
+
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "cube"
18
+ gem.homepage = "http://github.com/drkreso/cube"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Get's the data from OLAP cube via XMLA}
21
+ gem.description = %Q{Eases the pain I had to go through to get to the data of Xmla based OLAP provider}
22
+ gem.email = "kresimir.bojcic@gmail.com"
23
+ gem.authors = ["drKreso"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rake/rdoctask'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "cube #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,72 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "cube"
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["drKreso"]
12
+ s.date = "2012-02-09"
13
+ s.description = "Eases the pain I had to go through to get to the data of Xmla based OLAP provider"
14
+ s.email = "kresimir.bojcic@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "cube.gemspec",
29
+ "lib/cube.rb",
30
+ "lib/cube/cube.rb",
31
+ "lib/cube/xmla.rb",
32
+ "lib/wsdl/xmla.xml",
33
+ "spec/cassettes/kvartovi_u_recima.yml",
34
+ "spec/cassettes/kvatovi_u_koloni.yml",
35
+ "spec/cassettes/razlog_prijave_i_kvart.yml",
36
+ "spec/cube_spec.rb",
37
+ "spec/spec_helper.rb"
38
+ ]
39
+ s.homepage = "http://github.com/drkreso/cube"
40
+ s.licenses = ["MIT"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = "1.8.10"
43
+ s.summary = "Get's the data from OLAP cube via XMLA"
44
+
45
+ if s.respond_to? :specification_version then
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_runtime_dependency(%q<savon>, [">= 0"])
50
+ s.add_runtime_dependency(%q<webmock>, [">= 0"])
51
+ s.add_development_dependency(%q<vcr>, [">= 0"])
52
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
53
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
54
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
55
+ else
56
+ s.add_dependency(%q<savon>, [">= 0"])
57
+ s.add_dependency(%q<webmock>, [">= 0"])
58
+ s.add_dependency(%q<vcr>, [">= 0"])
59
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
60
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
61
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
62
+ end
63
+ else
64
+ s.add_dependency(%q<savon>, [">= 0"])
65
+ s.add_dependency(%q<webmock>, [">= 0"])
66
+ s.add_dependency(%q<vcr>, [">= 0"])
67
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
68
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
69
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
70
+ end
71
+ end
72
+
@@ -0,0 +1,2 @@
1
+ require_relative 'cube/xmla.rb'
2
+ require_relative 'cube/cube.rb'
@@ -0,0 +1,100 @@
1
+ require 'savon'
2
+
3
+ Savon.configure do |config|
4
+ config.soap_version = 1
5
+ config.log = false
6
+ end
7
+
8
+ HTTPI.log = false
9
+
10
+ module Xmla
11
+
12
+ class Cube
13
+
14
+ def Cube.execute(query, catalog = Xmla.catalog)
15
+ Cube.new(query, catalog).as_table
16
+ end
17
+
18
+ def as_table
19
+ clean_table(table, y_size).reduce([]) { |result, row| result << row.flatten.join('|') }
20
+ end
21
+
22
+ def x_axe() @x_axe ||= axes[0] end
23
+ def y_axe() @y_axe ||= axes[1] end
24
+ def y_size() y_axe[0].size end
25
+ def x_size() x_axe.size end
26
+
27
+ private
28
+
29
+ #header and rows
30
+ def table
31
+ (0...y_axe.size).reduce(header) { |result, j| result << ( y_axe[j] + (0...x_size).map { |i| "#{cell_data[i + j]}" }) }
32
+ end
33
+
34
+ def header
35
+ [ ( (0..y_size - 1).reduce([]) { |header| header << '' } << x_axe).flatten ]
36
+ end
37
+
38
+ def axes
39
+ axes = @response.to_hash[:execute_response][:return][:root][:axes][:axis].select { |axe| axe[:@name] != "SlicerAxis" }
40
+ @axes ||= axes.reduce([]) do |result, axe|
41
+ y = tuple(axe).reduce([]) { |y, member|
42
+ data = (member[0] == :member) ? member[1] : member[:member]
43
+ if data.class == Hash
44
+ y << [data[:caption].strip]
45
+ elsif data.size == 1
46
+ y << data[:caption].strip
47
+ else
48
+ z = []
49
+ data.each do |item_data|
50
+ if (item_data.class == Hash)
51
+ caption = item_data[:caption].strip
52
+ z << caption
53
+ end
54
+ end
55
+ y << z
56
+ end
57
+ }
58
+ result << y
59
+ end
60
+ end
61
+
62
+ def initialize(query, catalog)
63
+ @query = query
64
+ @catalog = catalog
65
+ @response = get_response
66
+ self
67
+ end
68
+
69
+ def get_response
70
+ client = Savon::Client.new do
71
+ wsdl.document = File.expand_path("../../wsdl/xmla.xml", __FILE__)
72
+ wsdl.endpoint = Xmla.endpoint
73
+ end
74
+
75
+ @response = client.request :execute, xmlns:"urn:schemas-microsoft-com:xml-analysis" do
76
+ soap.body = "<Command> <Statement> <![CDATA[ #{@query} ]]> </Statement> </Command> <Properties> <PropertyList> <Catalog>#{@catalog}</Catalog>
77
+ <Format>Multidimensional</Format> <AxisFormat>TupleFormat</AxisFormat> </PropertyList> </Properties>"
78
+ end
79
+ end
80
+
81
+ #cleanup table so items don't repeat (if they are same)
82
+ def clean_table(table, number_of_colums)
83
+ above_row = []
84
+ #filter if they are not last column, and they are same as the item on the row above
85
+ table.reduce([]) { |result, row|
86
+ result << row.each_with_index.map { |item,i| (i == number_of_colums) ? item : ((item == above_row[i]) ? '' : item ) }
87
+ above_row = row
88
+ result
89
+ }
90
+ end
91
+
92
+ def cell_data
93
+ cell_data = @response.to_hash[:execute_response][:return][:root][:cell_data]
94
+ @data ||= cell_data.reduce([]) { |data, cell| cell[1].reduce(data) { |data, value| data << value[:value] } }
95
+ end
96
+
97
+ def tuple(axe) axe[:tuples][:tuple] end
98
+
99
+ end
100
+ end
@@ -0,0 +1,16 @@
1
+ module Xmla
2
+ def self.configure
3
+ yield self if block_given?
4
+ end
5
+
6
+ def self.endpoint=(value)
7
+ @endpoint = value
8
+ end
9
+
10
+ def self.catalog=(value)
11
+ @catalog = value
12
+ end
13
+
14
+ def self.endpoint() @endpoint end
15
+ def self.catalog() @catalog end
16
+ end
@@ -0,0 +1,104 @@
1
+ <?xml version='1.0' encoding='UTF-8' ?>
2
+ <!-- Generated 04/26/01 by Microsoft SOAP Toolkit WSDL File Generator, Version 1.00.623.0 -->
3
+ <definitions name ='msxmla' targetNamespace = 'http://tempuri.org/wsdl/'
4
+ xmlns:wsdlns='http://tempuri.org/wsdl/'
5
+ xmlns:typens='http://tempuri.org/type'
6
+ xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
7
+ xmlns:xsd='http://www.w3.org/2001/XMLSchema'
8
+ xmlns:stk='http://schemas.microsoft.com/soap-toolkit/wsdl-extension'
9
+ xmlns='http://schemas.xmlsoap.org/wsdl/'>
10
+ <types>
11
+ <schema targetNamespace='http://tempuri.org/type'
12
+ xmlns='http://www.w3.org/2001/XMLSchema'
13
+ xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/'
14
+ xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'
15
+ elementFormDefault='qualified'>
16
+ <complexType name ='clsXMLAProx.Discover.Result'>
17
+ <sequence>
18
+ <any minOccurs='0' maxOccurs='unbounded' namespace='#any' processContents='skip'/>
19
+ </sequence>
20
+ </complexType>
21
+ <complexType name ='clsXMLAProx.Discover.Restrictions'>
22
+ <sequence>
23
+ <any minOccurs='0' maxOccurs='unbounded' namespace='#any' processContents='skip'/>
24
+ </sequence>
25
+ </complexType>
26
+ <complexType name ='clsXMLAProx.Discover.Properties'>
27
+ <sequence>
28
+ <any minOccurs='0' maxOccurs='unbounded' namespace='#any' processContents='skip'/>
29
+ </sequence>
30
+ </complexType>
31
+ <complexType name ='clsXMLAProx.Execute.Result'>
32
+ <sequence>
33
+ <any minOccurs='0' maxOccurs='unbounded' namespace='#any' processContents='skip'/>
34
+ </sequence>
35
+ </complexType>
36
+ <complexType name ='clsXMLAProx.Execute.Command'>
37
+ <sequence>
38
+ <any minOccurs='0' maxOccurs='unbounded' namespace='#any' processContents='skip'/>
39
+ </sequence>
40
+ </complexType>
41
+ <complexType name ='clsXMLAProx.Execute.Properties'>
42
+ <sequence>
43
+ <any minOccurs='0' maxOccurs='unbounded' namespace='#any' processContents='skip'/>
44
+ </sequence>
45
+ </complexType>
46
+ </schema>
47
+ </types>
48
+ <message name='clsXMLAProx.Discover'>
49
+ <part name='RequestType' type='xsd:string'/>
50
+ <part name='Restrictions' type='typens:clsXMLAProx.Discover.Restrictions'/>
51
+ <part name='Properties' type='typens:clsXMLAProx.Discover.Properties'/>
52
+ </message>
53
+ <message name='clsXMLAProx.DiscoverResponse'>
54
+ <part name='Result' type='typens:clsXMLAProx.Discover.Result'/>
55
+ </message>
56
+ <message name='clsXMLAProx.Execute'>
57
+ <part name='Command' type='typens:clsXMLAProx.Execute.Command'/>
58
+ <part name='Properties' type='typens:clsXMLAProx.Execute.Properties'/>
59
+ </message>
60
+ <message name='clsXMLAProx.ExecuteResponse'>
61
+ <part name='Result' type='typens:clsXMLAProx.Execute.Result'/>
62
+ </message>
63
+ <portType name='clsXMLAProxSoapPort'>
64
+ <operation name='Discover' parameterOrder='RequestType Restrictions Properties'>
65
+ <input message='wsdlns:clsXMLAProx.Discover' />
66
+ <output message='wsdlns:clsXMLAProx.DiscoverResponse' />
67
+ </operation>
68
+ <operation name='Execute' parameterOrder='Command Properties'>
69
+ <input message='wsdlns:clsXMLAProx.Execute' />
70
+ <output message='wsdlns:clsXMLAProx.ExecuteResponse' />
71
+ </operation>
72
+ </portType>
73
+ <binding name='clsXMLAProxSoapBinding' type='wsdlns:clsXMLAProxSoapPort' >
74
+ <stk:binding preferredEncoding='UTF-8'/>
75
+ <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http' />
76
+ <operation name='Discover' >
77
+ <soap:operation soapAction='urn:schemas-microsoft-com:xml-analysis:Discover' />
78
+ <input>
79
+ <soap:body use='encoded' namespace='urn:schemas-microsoft-com:xml-analysis'
80
+ encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' />
81
+ </input>
82
+ <output>
83
+ <soap:body use='encoded' namespace='urn:schemas-microsoft-com:xml-analysis'
84
+ encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' />
85
+ </output>
86
+ </operation>
87
+ <operation name='Execute' >
88
+ <soap:operation soapAction='urn:schemas-microsoft-com:xml-analysis:Execute' />
89
+ <input>
90
+ <soap:body use='encoded' namespace='urn:schemas-microsoft-com:xml-analysis'
91
+ encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' />
92
+ </input>
93
+ <output>
94
+ <soap:body use='encoded' namespace='urn:schemas-microsoft-com:xml-analysis'
95
+ encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' />
96
+ </output>
97
+ </operation>
98
+ </binding>
99
+ <service name='msxmla' >
100
+ <port name='clsXMLAProxSoapPort' binding='wsdlns:clsXMLAProxSoapBinding' >
101
+ <soap:address location='http://localhost/xmla/msxisapi.dll' />
102
+ </port>
103
+ </service>
104
+ </definitions>