olap-xmla 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +1 -0
- data/README.md +79 -2
- data/lib/olap/xmla.rb +42 -2
- data/lib/olap/xmla/client.rb +74 -0
- data/lib/olap/xmla/response.rb +154 -0
- data/lib/olap/xmla/version.rb +1 -1
- data/olap-xmla.gemspec +4 -1
- data/spec/gem_spec.rb +72 -0
- data/spec/spec_helper.rb +11 -0
- metadata +51 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 353d1787b60fcbfdb612748987277c3d3b4046ea
|
4
|
+
data.tar.gz: b7e723cf2067e094e4ba74e51267b3b3c2cd2048
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9558ccbf3c6d913a7074ea34b6bb722bc5b75983e0dff1890ce98bbd45f2f1d000ada9ce790493988d8da66993439ff8dedd8062425d07f1e295954e0312a896
|
7
|
+
data.tar.gz: ddafa507a59edd7a7cab7035c5389672d9b156594b0550726b5ef36894710500407eb93757b536d42590a128ede68a018668a4383ac605263af5f36bb6988c5d
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Olap::Xmla
|
2
2
|
|
3
|
-
|
3
|
+
The gem connects to OLAP database using XMLA interface and executes MDX queries.
|
4
|
+
|
5
|
+
Can be used in Ruby or Rails applications to display and analyse data from OLAP databases.
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
@@ -20,7 +22,82 @@ Or install it yourself as:
|
|
20
22
|
|
21
23
|
## Usage
|
22
24
|
|
23
|
-
|
25
|
+
Using the gem is very simple, only basic knowledge on OLAP is required.
|
26
|
+
|
27
|
+
### Connecting to server
|
28
|
+
|
29
|
+
To use the gem, you need to know the connection requisites to connect to XMLA server:
|
30
|
+
|
31
|
+
1. Server URL ( typically, http: or https: URL )
|
32
|
+
2. Datasource and catalog names. You can check them in your XMLA server configuration
|
33
|
+
|
34
|
+
Connecting to the server and executing MDX are straightforward:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require 'olap/xmla'
|
38
|
+
|
39
|
+
client = Olap::Xmla.client({
|
40
|
+
server: 'http://your-olap-server',
|
41
|
+
datasource: 'your-datasource',
|
42
|
+
catalog: 'your-catalog'})
|
43
|
+
response = client.request 'your-mdx-here'
|
44
|
+
```
|
45
|
+
|
46
|
+
### Configuration in Rails
|
47
|
+
|
48
|
+
If you are using this gem in Rails application, which uses just single OLAP data source,
|
49
|
+
you can simplify the code by pre-configuring the XMLA connection.
|
50
|
+
|
51
|
+
Create a file olap.rb in config/initializers directory with the following content:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
Olap::Xmla.default_options= { server: 'http://your-olap-server',
|
55
|
+
datasource: 'your-datasource',
|
56
|
+
catalog: 'your-catalog'}
|
57
|
+
```
|
58
|
+
|
59
|
+
Then in Rails application code you can simply do:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
response = Olap::Xmla.client.request 'your-mdx-here'
|
63
|
+
```
|
64
|
+
|
65
|
+
### Querying MDX
|
66
|
+
|
67
|
+
The gem does not parse MDX, just passes it to XMLA server.
|
68
|
+
|
69
|
+
However, it can do substituting parameters in the query:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
MDX_QUERY = 'SET [~ROWS_Date] AS {[DateTime].[Date].[Date].[%DATE%]}'
|
73
|
+
|
74
|
+
Olap::Xmla.client.request MDX_QUERY, {'%DATE%' => '20150530'}
|
75
|
+
```
|
76
|
+
|
77
|
+
This allows to store MDX queries in constants, while execute them with dynamic parameters.
|
78
|
+
Note, that you should never use these parameters directly from Rails request, as
|
79
|
+
this may create security breach!
|
80
|
+
|
81
|
+
### Using response on Query
|
82
|
+
|
83
|
+
|
84
|
+
You may use the response to render the results to user, or post-process it to analyse the data
|
85
|
+
The following methods can be used to request the meta-data and data from the response:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
response = client.request(mdx)
|
89
|
+
|
90
|
+
|
91
|
+
# Meta - data of the response
|
92
|
+
response.measures # array of the columns definitions ( :name / :caption )
|
93
|
+
response.dimensions # array of the rows definitions ( :name )
|
94
|
+
|
95
|
+
# Response data
|
96
|
+
response.rows # rows of the response
|
97
|
+
response.to_hash # response as a hash
|
98
|
+
response.column_values(column_num) # just one column of the response
|
99
|
+
|
100
|
+
```
|
24
101
|
|
25
102
|
## Contributing
|
26
103
|
|
data/lib/olap/xmla.rb
CHANGED
@@ -1,10 +1,50 @@
|
|
1
1
|
require "olap/xmla/version"
|
2
|
+
require "olap/xmla/client"
|
2
3
|
|
3
4
|
module Olap
|
5
|
+
|
6
|
+
#
|
7
|
+
# Connects to XMLA[https://en.wikipedia.org/wiki/XML_for_Analysis] server and executes MDX[https://en.wikipedia.org/wiki/MultiDimensional_eXpressions] queries
|
8
|
+
#
|
4
9
|
module Xmla
|
5
10
|
|
6
|
-
|
7
|
-
|
11
|
+
@@connect_options = {}
|
12
|
+
|
13
|
+
# Configure the default options to connect to XMLA server
|
14
|
+
# Can be optionally used to setup connection options in one place in application,
|
15
|
+
#
|
16
|
+
# Example:
|
17
|
+
# >> Olap::Xmla.default_options = {server: 'http://your-olap-server', datasource: 'your-datasource', catalog: 'your-catalog'}
|
18
|
+
# >> Olap::Xmla.client.request mdx
|
19
|
+
# => #<Olap::Xmla::Response:0x000001035b9510 @response={ ...
|
20
|
+
#
|
21
|
+
# Look client connect_options for the list of options to be specified
|
22
|
+
#
|
23
|
+
def self.default_options= options
|
24
|
+
@@connect_options = options
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create a client, which can be used then to execute MDX queries
|
28
|
+
#
|
29
|
+
# Example:
|
30
|
+
# >> client = Olap::Xmla.client(server: 'http://your-olap-server', datasource: 'your-datasource', catalog: 'your-catalog')
|
31
|
+
# >> response = client.request mdx
|
32
|
+
# => #<Olap::Xmla::Response:0x000001035b9510 @response={ ...
|
33
|
+
#
|
34
|
+
# ==== connect_options
|
35
|
+
#
|
36
|
+
# * +:server+ - URL to connect to XMLA server (required)
|
37
|
+
# * +:datasource+ - the name of datasource (required)
|
38
|
+
# * +:catalog+ - the name of catalog (required)
|
39
|
+
# * +:open_timeout+ - open timeout to connect to XMLA server, optional, default is 60 sec
|
40
|
+
# * +:read_timeout+ - open timeout to read data from XMLA server, optional, default is 300 sec
|
41
|
+
#
|
42
|
+
#
|
43
|
+
def self.client connect_options = {}
|
44
|
+
options = @@connect_options.merge connect_options
|
45
|
+
raise "Connect options must define :server, :datasource and :catalog options" unless
|
46
|
+
options[:server] && options[:datasource] && options[:catalog]
|
47
|
+
Olap::Xmla::Client.new options[:server], options[:datasource], options[:catalog], options
|
8
48
|
end
|
9
49
|
|
10
50
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'savon'
|
2
|
+
require 'olap/xmla/response'
|
3
|
+
|
4
|
+
class Olap::Xmla::Client
|
5
|
+
|
6
|
+
attr_reader :data_source, :catalog, :client
|
7
|
+
|
8
|
+
def initialize server, data_source, catalog, options
|
9
|
+
|
10
|
+
@catalog = catalog
|
11
|
+
@data_source = data_source
|
12
|
+
@client = Savon.client do
|
13
|
+
endpoint server
|
14
|
+
namespace "urn:schemas-microsoft-com:xml-analysis"
|
15
|
+
open_timeout (options[:open_timeout] || 60)
|
16
|
+
read_timeout (options[:read_timeout] || 300)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Executes multiple MDX queries as a batch
|
21
|
+
#
|
22
|
+
# Arguments:
|
23
|
+
# * +mdx_requests+ - Collection of MDX requests
|
24
|
+
# * +parameters+ - Map of parameters to substitute in MDX request, optional
|
25
|
+
#
|
26
|
+
def batch mdx_requests, parameters = {}
|
27
|
+
mdx_requests.collect{|mdx|
|
28
|
+
request mdx, parameters
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
# Execute MDX queries, substituting parameters in the query
|
33
|
+
#
|
34
|
+
# Arguments:
|
35
|
+
# * +mdx_request+ - MDX request as a string, required
|
36
|
+
# * +parameters+ - Map of parameters to substitute in MDX request, optional
|
37
|
+
#
|
38
|
+
# Example:
|
39
|
+
#
|
40
|
+
# client.request 'SET [~ROWS_Date] AS {[DateTime].[Date].[Date].[%DATE%]}', {'%DATE%' => '20150530'}
|
41
|
+
# will execute actual MDX: SET [~ROWS_Date] AS {[DateTime].[Date].[Date].[20150530]}
|
42
|
+
#
|
43
|
+
#
|
44
|
+
def request mdx_request, parameters = {}
|
45
|
+
|
46
|
+
mdx = mdx_request.clone
|
47
|
+
|
48
|
+
parameters.each{|k,v|
|
49
|
+
mdx.gsub!(k,v)
|
50
|
+
}
|
51
|
+
|
52
|
+
ops = client.operation('Execute')
|
53
|
+
p = { 'wsdl:PropertyList' => {'wsdl:DataSourceInfo' => data_source,
|
54
|
+
'wsdl:Catalog' => catalog,
|
55
|
+
'wsdl:Format' => 'Multidimensional',
|
56
|
+
'wsdl:AxisFormat'=> 'TupleFormat'} }
|
57
|
+
|
58
|
+
|
59
|
+
r = ops.call( message: {'wsdl:Command' => { 'wsdl:Statement' => mdx}, 'wsdl:Properties' => p })
|
60
|
+
|
61
|
+
unless r.success?
|
62
|
+
raise "Error executing #{mdx} in #{catalog} #{data_source}: #{r.http_error} #{r.soap_fault}"
|
63
|
+
end
|
64
|
+
|
65
|
+
Olap::Xmla::Response.new r.body[:execute_response][:return][:root]
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
|
@@ -0,0 +1,154 @@
|
|
1
|
+
class Olap::Xmla::Response
|
2
|
+
|
3
|
+
attr_reader :response
|
4
|
+
|
5
|
+
def initialize response
|
6
|
+
@response = response
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns true if the response has any data
|
10
|
+
def has_data?
|
11
|
+
not response[:cell_data][:cell].empty?
|
12
|
+
end
|
13
|
+
|
14
|
+
# Collection of measures in response
|
15
|
+
#
|
16
|
+
# * +:name+ the name of measure
|
17
|
+
# * +:caption+ display name of measure
|
18
|
+
#
|
19
|
+
def measures
|
20
|
+
response[:axes][:axis][0][:tuples][:tuple].collect{|m|
|
21
|
+
{
|
22
|
+
name: m[:member][:u_name],
|
23
|
+
caption: m[:member][:caption]
|
24
|
+
}
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Collection of dimensions in response
|
29
|
+
#
|
30
|
+
# * +:name+ the name of dimension
|
31
|
+
#
|
32
|
+
def dimensions
|
33
|
+
response[:olap_info][:axes_info][:axis_info][1][:hierarchy_info].collect{|m|
|
34
|
+
{
|
35
|
+
name: m[:@name]
|
36
|
+
}
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Collection of result rows
|
42
|
+
# * rownum number of row ( 1... N)
|
43
|
+
# * labels - array of tuples per the row
|
44
|
+
# value - the value of tuple
|
45
|
+
# name - the name of tuple
|
46
|
+
# * values - the values array
|
47
|
+
# measure - metric name
|
48
|
+
# value - metric value
|
49
|
+
# fmt_value - formatted metric value
|
50
|
+
# colnum - column number ( 1..N)
|
51
|
+
def rows
|
52
|
+
|
53
|
+
return [] unless response[:cell_data]
|
54
|
+
|
55
|
+
measures = [response[:axes][:axis][0][:tuples][:tuple]].flatten
|
56
|
+
cells = [response[:cell_data][:cell]].flatten
|
57
|
+
|
58
|
+
cell_ordinal = 0
|
59
|
+
cell_index = 0
|
60
|
+
rownum = 0
|
61
|
+
|
62
|
+
[response[:axes][:axis][1][:tuples][:tuple]].flatten.collect{ |tuple|
|
63
|
+
rownum += 1
|
64
|
+
colnum = 0
|
65
|
+
tuple_member = [tuple[:member]].flatten
|
66
|
+
{ rownum: rownum,
|
67
|
+
labels: tuple_member.collect{|member|
|
68
|
+
value = member[:caption]
|
69
|
+
value = nil if value=='#null'
|
70
|
+
{value: value, name: member[:@hierarchy]}
|
71
|
+
},
|
72
|
+
values: measures.collect{|m|
|
73
|
+
|
74
|
+
colnum += 1
|
75
|
+
|
76
|
+
if (cell=cells[cell_index]) && cell[:@cell_ordinal].to_i==cell_ordinal
|
77
|
+
cell_index += 1
|
78
|
+
cell_ordinal += 1
|
79
|
+
{ colnum: colnum, measure: m[:member][:u_name], value: cell[:value], fmt_value: cell[:fmt_value]}
|
80
|
+
else
|
81
|
+
cell_ordinal += 1
|
82
|
+
{ colnum: colnum, measure: m[:member][:u_name], value: nil, fmt_value: nil}
|
83
|
+
end
|
84
|
+
|
85
|
+
}
|
86
|
+
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
# query a colum of the result
|
93
|
+
#
|
94
|
+
# Example:
|
95
|
+
#
|
96
|
+
# >> response.column_values 1
|
97
|
+
# => [30.0, 1025.0, 884.0, 543.0,...
|
98
|
+
#
|
99
|
+
def column_values column_num
|
100
|
+
|
101
|
+
rows.collect{|row|
|
102
|
+
row[:values].detect{|value|
|
103
|
+
value[:colnum]==column_num
|
104
|
+
}[:value].to_f
|
105
|
+
}
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
# Aggregate result by one of the columns
|
110
|
+
def column_values_aggregate dimension_aggr_index = 0
|
111
|
+
|
112
|
+
result = []
|
113
|
+
index = {}
|
114
|
+
|
115
|
+
rows.each{|row|
|
116
|
+
label = row[:labels][dimension_aggr_index]
|
117
|
+
|
118
|
+
if i = index[label]
|
119
|
+
for j in 0..result[i][:values].count-1
|
120
|
+
result[i][:values][j] += row[:values][j][:value].to_f
|
121
|
+
end
|
122
|
+
else
|
123
|
+
index[label] = result.count
|
124
|
+
result << {
|
125
|
+
rownum: result.count + 1,
|
126
|
+
label: label,
|
127
|
+
values: row[:values].collect{|v| v[:value].to_f }
|
128
|
+
}
|
129
|
+
end
|
130
|
+
}
|
131
|
+
|
132
|
+
result
|
133
|
+
end
|
134
|
+
|
135
|
+
# Convert the response to hash
|
136
|
+
# keys of hash are values of tuples
|
137
|
+
# values of hash are values of metrics
|
138
|
+
#
|
139
|
+
# Example:
|
140
|
+
#
|
141
|
+
# response.to_hash
|
142
|
+
# => {["2014", "05/11/2014"]=>[30.0, 27.0, 0.0], ["2014", "06/11/2014"]=>[1025.0, 688.0, 73.0]
|
143
|
+
#
|
144
|
+
def to_hash
|
145
|
+
Hash[rows.collect{|row|
|
146
|
+
[
|
147
|
+
row[:labels].collect{|l| l[:value].nil? ? nil : l[:value] },
|
148
|
+
row[:values].collect{|l| (l.nil? || l[:value].nil?) ? 0.0 : l[:value].to_f },
|
149
|
+
]
|
150
|
+
|
151
|
+
}]
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
data/lib/olap/xmla/version.rb
CHANGED
data/olap-xmla.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["aleksey@wondersoft.ru"]
|
11
11
|
spec.summary = %q{Pure Ruby gem to make MDX queries on OLAP databases using XMLA connection}
|
12
12
|
spec.description = %q{Can be used with any XMLA-compliant server, like Olaper or Mondrian.}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/Wondersoft/olap-xmla"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -20,4 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.7"
|
22
22
|
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec", '~> 0'
|
24
|
+
spec.add_runtime_dependency 'savon', '~> 2.8'
|
25
|
+
spec.add_runtime_dependency 'rubyntlm', '~> 0.3.2'
|
23
26
|
end
|
data/spec/gem_spec.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
connection_config = YAML.load File.read 'spec/.config.yaml'
|
5
|
+
|
6
|
+
describe 'Test MDX queries' do
|
7
|
+
|
8
|
+
it 'should request mdx' do
|
9
|
+
|
10
|
+
client = Olap::Xmla.client connection_config
|
11
|
+
|
12
|
+
mdx= "WITH SET [~ROWS_YearByDay] AS " +
|
13
|
+
"{[DateTime].[YearByDay].[Year].Members} " +
|
14
|
+
"SET [~ROWS_Date] AS " +
|
15
|
+
" {[DateTime].[Date].[Date].Members} " +
|
16
|
+
"SELECT " +
|
17
|
+
"NON EMPTY {[Measures].[Slots], [Measures].[Clicks], [Measures].[Conversions]} ON COLUMNS, " +
|
18
|
+
"NON EMPTY NonEmptyCrossJoin([~ROWS_YearByDay], [~ROWS_Date]) ON ROWS " +
|
19
|
+
"FROM [Ad Serving]"
|
20
|
+
|
21
|
+
response = client.request mdx
|
22
|
+
|
23
|
+
expect(response.has_data?).to be true
|
24
|
+
|
25
|
+
expect(response.measures.count).to eq 3
|
26
|
+
expect(response.measures[0][:name]).to eq '[Measures].[Slots]'
|
27
|
+
expect(response.measures[1][:name]).to eq '[Measures].[Clicks]'
|
28
|
+
|
29
|
+
expect(response.dimensions.count).to eq 2
|
30
|
+
|
31
|
+
expect(response.dimensions[0][:name]).to eq 'YearByDay'
|
32
|
+
expect(response.dimensions[1][:name]).to eq 'Date'
|
33
|
+
|
34
|
+
expect(response.rows.count).to be > 100
|
35
|
+
|
36
|
+
expect(response.rows[0][:rownum]).to eq 1
|
37
|
+
|
38
|
+
expect(response.rows[0][:labels].size).to eq 2
|
39
|
+
expect(response.rows[0][:labels][0][:name]).to eq 'YearByDay'
|
40
|
+
expect(response.rows[0][:labels][1][:name]).to eq 'Date'
|
41
|
+
|
42
|
+
expect(response.rows[0][:values].size).to eq 3
|
43
|
+
|
44
|
+
expect(response.rows[0][:values][0][:colnum]).to eq 1
|
45
|
+
expect(response.rows[0][:values][0][:measure]).to eq '[Measures].[Slots]'
|
46
|
+
expect(response.rows[0][:values][0][:value]).to_not be_nil
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should use default connect' do
|
51
|
+
|
52
|
+
Olap::Xmla.default_options = connection_config
|
53
|
+
|
54
|
+
client = Olap::Xmla.client
|
55
|
+
|
56
|
+
mdx= "WITH SET [~ROWS_YearByDay] AS " +
|
57
|
+
"{[DateTime].[YearByDay].[Year].Members} " +
|
58
|
+
"SET [~ROWS_Date] AS " +
|
59
|
+
" {[DateTime].[Date].[Date].Members} " +
|
60
|
+
"SELECT " +
|
61
|
+
"NON EMPTY {[Measures].[Slots], [Measures].[Clicks], [Measures].[Conversions]} ON COLUMNS, " +
|
62
|
+
"NON EMPTY NonEmptyCrossJoin([~ROWS_YearByDay], [~ROWS_Date]) ON ROWS " +
|
63
|
+
"FROM [Ad Serving]"
|
64
|
+
|
65
|
+
response = client.request mdx
|
66
|
+
|
67
|
+
expect(response.has_data?).to be true
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: olap-xmla
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- studnev
|
@@ -38,6 +38,48 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: savon
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.8'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubyntlm
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.3.2
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.3.2
|
41
83
|
description: Can be used with any XMLA-compliant server, like Olaper or Mondrian.
|
42
84
|
email:
|
43
85
|
- aleksey@wondersoft.ru
|
@@ -51,9 +93,13 @@ files:
|
|
51
93
|
- README.md
|
52
94
|
- Rakefile
|
53
95
|
- lib/olap/xmla.rb
|
96
|
+
- lib/olap/xmla/client.rb
|
97
|
+
- lib/olap/xmla/response.rb
|
54
98
|
- lib/olap/xmla/version.rb
|
55
99
|
- olap-xmla.gemspec
|
56
|
-
|
100
|
+
- spec/gem_spec.rb
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
homepage: https://github.com/Wondersoft/olap-xmla
|
57
103
|
licenses:
|
58
104
|
- MIT
|
59
105
|
metadata: {}
|
@@ -77,4 +123,6 @@ rubygems_version: 2.2.2
|
|
77
123
|
signing_key:
|
78
124
|
specification_version: 4
|
79
125
|
summary: Pure Ruby gem to make MDX queries on OLAP databases using XMLA connection
|
80
|
-
test_files:
|
126
|
+
test_files:
|
127
|
+
- spec/gem_spec.rb
|
128
|
+
- spec/spec_helper.rb
|