multidimensional_table 1.0.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/.gitignore +18 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +79 -0
- data/Rakefile +2 -0
- data/lib/multidimensional_table/multidimensional_table.rb +72 -0
- data/lib/multidimensional_table/non_existant_dimension_attribute.rb +12 -0
- data/lib/multidimensional_table/non_valid_dimension.rb +18 -0
- data/lib/multidimensional_table/version.rb +3 -0
- data/lib/multidimensional_table.rb +3 -0
- data/multidimensional_table.gemspec +17 -0
- data/spec/multidimensional_table/multidimensional_table_spec.rb +130 -0
- metadata +59 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 drKreso
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# MultidimensionalTable
|
2
|
+
|
3
|
+
This is a product of a coding kata. I wanted an easy way to describe multidimensional table
|
4
|
+
but only using ruby code and not some sort of .yaml or other custom format that you have to parse in some way.
|
5
|
+
|
6
|
+
For example:
|
7
|
+
|
8
|
+
```
|
9
|
+
1994
|
10
|
+
BuenosAires
|
11
|
+
Coal
|
12
|
+
19t
|
13
|
+
Potassium
|
14
|
+
5t
|
15
|
+
1995
|
16
|
+
BuenosAires
|
17
|
+
Coal
|
18
|
+
8t
|
19
|
+
Potassium
|
20
|
+
6t
|
21
|
+
```
|
22
|
+
|
23
|
+
Then if I say I need to get Coal in Buenos Aires in 1995 I wan't to get 8t. The idea is to make easy DSL to
|
24
|
+
describe the same thing in pure Ruby.
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
Add this line to your application's Gemfile:
|
29
|
+
|
30
|
+
gem 'multidimensional_table'
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
$ bundle
|
35
|
+
|
36
|
+
Or install it yourself as:
|
37
|
+
|
38
|
+
$ gem install multidimensional_table
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
```
|
43
|
+
class MaterialConsumption
|
44
|
+
include MultidimensionalTable
|
45
|
+
|
46
|
+
dimensions :year => [:year_1994, :year_1995],
|
47
|
+
:city => [:buenos_aires],
|
48
|
+
:material => [:coal, :potassium]
|
49
|
+
|
50
|
+
table_data do
|
51
|
+
year_1994 do
|
52
|
+
buenos_aires do
|
53
|
+
coal '8t'
|
54
|
+
potassium '5t'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
year_1995 do
|
59
|
+
buenos_aires do
|
60
|
+
coal '8t'
|
61
|
+
potassium '6t'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
mt = new MaterialConsumption
|
69
|
+
mt.update_attributes(:year => :year_1994, :city => :buenos_aires, :material => :coal)
|
70
|
+
mt.table_result #=> '8t'
|
71
|
+
```
|
72
|
+
|
73
|
+
## Contributing
|
74
|
+
|
75
|
+
1. Fork it
|
76
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
77
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
78
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
79
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'multidimensional_table/non_existant_dimension_attribute'
|
2
|
+
require 'multidimensional_table/non_valid_dimension'
|
3
|
+
|
4
|
+
module MultidimensionalTable
|
5
|
+
|
6
|
+
def dimensions=(map)
|
7
|
+
@dimensions = map
|
8
|
+
check_duplicates(map)
|
9
|
+
@dimensions.each do |key, value|
|
10
|
+
value.each do |possible_value|
|
11
|
+
define_method possible_value do |value = nil, &block|
|
12
|
+
if value.nil? && !block.nil?
|
13
|
+
@index_level += 1
|
14
|
+
@context[@index_level] = "@attributes[:#{key}] == :#{possible_value}"
|
15
|
+
begin
|
16
|
+
block.call
|
17
|
+
rescue NoMethodError => e
|
18
|
+
raise NonExistantDimensionAttribute.new(e.name)
|
19
|
+
end
|
20
|
+
@index_level -= 1
|
21
|
+
elsif !value.nil?
|
22
|
+
context = (1..@index_level).reduce([]) { |context, level| context << @context[level] }
|
23
|
+
@table_rules[value] = context << ["@attributes[:#{key}] == :#{possible_value}"]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def check_duplicates(map)
|
31
|
+
list = {}
|
32
|
+
combined = map.each_value.reduce([]) { |all, value| all << value }.flatten
|
33
|
+
combined.each do |item|
|
34
|
+
if list[item].nil?
|
35
|
+
list[item] = 1
|
36
|
+
else
|
37
|
+
list[item] = list[item] + 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
duplicates = list.select { |key, value| key if value > 1 }
|
41
|
+
if duplicates != {}
|
42
|
+
non_valid_dimensions = {}
|
43
|
+
duplicates.each_key do |duplicate|
|
44
|
+
non_valid_dimensions[duplicate] = []
|
45
|
+
map.each do |key,value|
|
46
|
+
non_valid_dimensions[duplicate] << key if value.include?(duplicate)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
raise NonValidDimension, non_valid_dimensions
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def update_attributes(attrs)
|
54
|
+
attrs.each do |key, value| @attributes[key] = value end
|
55
|
+
end
|
56
|
+
|
57
|
+
def table_result
|
58
|
+
@table_rules.each { |key, condition| return key if class_eval(condition.join(' && ')) == true }
|
59
|
+
end
|
60
|
+
|
61
|
+
def table_data
|
62
|
+
@context = []
|
63
|
+
@table_rules = {}
|
64
|
+
@attributes ||= {}
|
65
|
+
@index_level = 0
|
66
|
+
yield
|
67
|
+
end
|
68
|
+
|
69
|
+
def dimensions
|
70
|
+
@dimensions ||= {}
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MultidimensionalTable
|
2
|
+
class NonValidDimension < StandardError
|
3
|
+
attr_accessor :duplicate
|
4
|
+
def initialize(duplicate)
|
5
|
+
@duplicate = duplicate
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
"Multiple definitions are not allowed : #{represent_duplicates}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def represent_duplicates
|
13
|
+
duplicate.map do |key, value|
|
14
|
+
"#{key} for dimension " << value.join(' and ')
|
15
|
+
end.join(', ')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/multidimensional_table/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["drKreso"]
|
6
|
+
gem.email = ["kresimir.bojcic@gmail.com"]
|
7
|
+
gem.description = %q{Describe a multidimensional table in pure Ruby}
|
8
|
+
gem.summary = %q{Attempt to make generic API for describing multidimensional data in Ruby}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "multidimensional_table"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = MultidimensionalTable::VERSION
|
17
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'multidimensional_table/multidimensional_table'
|
2
|
+
|
3
|
+
|
4
|
+
module MultidimensionalTable
|
5
|
+
describe MultidimensionalTable do
|
6
|
+
context 'basic setup' do
|
7
|
+
it 'knows dimenions it has' do
|
8
|
+
subject.extend(MultidimensionalTable)
|
9
|
+
subject.dimensions = { :material => [:potassium, :coal, :sugar] }
|
10
|
+
subject.dimensions[:material].should == [:potassium, :coal, :sugar]
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'creates methods for dimensions' do
|
14
|
+
subject.extend(MultidimensionalTable)
|
15
|
+
subject.dimensions = { :material => [:potassium, :coal, :sugar] }
|
16
|
+
subject.respond_to?(:potassium).should == true
|
17
|
+
subject.respond_to?(:coal).should == true
|
18
|
+
subject.respond_to?(:sugar).should == true
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should be able to find data from table' do
|
22
|
+
subject.extend(MultidimensionalTable)
|
23
|
+
subject.dimensions = { :material => [:potassium, :coal, :sugar] }
|
24
|
+
subject.table_data do
|
25
|
+
subject.coal '8t'
|
26
|
+
subject.sugar '9t'
|
27
|
+
end
|
28
|
+
subject.update_attributes(:material => :coal)
|
29
|
+
subject.table_result.should == '8t'
|
30
|
+
subject.update_attributes(:material => :sugar)
|
31
|
+
subject.table_result.should == '9t'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'two dimensional' do
|
36
|
+
before(:each) do
|
37
|
+
subject.extend(MultidimensionalTable)
|
38
|
+
subject.dimensions = { :material => [:potassium, :coal, :sugar], :city => [:zagreb, :zadar] }
|
39
|
+
subject.table_data do
|
40
|
+
subject.zagreb do
|
41
|
+
subject.coal '8t'
|
42
|
+
subject.sugar '9t'
|
43
|
+
end
|
44
|
+
subject.zadar do
|
45
|
+
subject.coal '1t'
|
46
|
+
subject.sugar '2t'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should be able to pinpoint coal in zagreb' do
|
52
|
+
subject.update_attributes(:material => :coal, :city => :zagreb)
|
53
|
+
subject.table_result.should == '8t'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should be able to pinpoint sugar in zadar' do
|
57
|
+
subject.update_attributes(:material => :sugar, :city => :zadar)
|
58
|
+
subject.table_result.should == '2t'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'three dimensional' do
|
63
|
+
before(:each) do
|
64
|
+
subject.extend(MultidimensionalTable)
|
65
|
+
subject.dimensions = { :material => [:potassium, :coal, :sugar],
|
66
|
+
:city => [:zagreb, :zadar],
|
67
|
+
:time_of_day => [:morning, :evening] }
|
68
|
+
subject.table_data do
|
69
|
+
subject.zagreb do
|
70
|
+
subject.morning do
|
71
|
+
subject.coal '8t'
|
72
|
+
subject.sugar '9t'
|
73
|
+
end
|
74
|
+
subject.evening do
|
75
|
+
subject.coal '4t'
|
76
|
+
subject.sugar '5t'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
subject.zadar do
|
80
|
+
subject.coal '1t'
|
81
|
+
subject.sugar '2t'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should be able to pinpoint coal in zagreb at evening' do
|
87
|
+
subject.update_attributes(:material => :coal, :city => :zagreb, :time_of_day => :evening)
|
88
|
+
subject.table_result.should == '4t'
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should be able to pinpoint sugar in zagreb at morning' do
|
92
|
+
subject.update_attributes(:material => :sugar, :city => :zagreb, :time_of_day => :morning)
|
93
|
+
subject.table_result.should == '9t'
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should be able to pinpoint sugar in zadar' do
|
97
|
+
subject.update_attributes(:material => :sugar, :city => :zadar)
|
98
|
+
subject.table_result.should == '2t'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should complain if dimension does not exist' do
|
103
|
+
subject.extend(MultidimensionalTable)
|
104
|
+
subject.dimensions = { :material => [:potassium, :coal, :sugar],
|
105
|
+
:city => [:zagreb, :zadar],
|
106
|
+
:time_of_day => [:morning, :evening] }
|
107
|
+
|
108
|
+
expect do
|
109
|
+
subject.table_data do
|
110
|
+
subject.zagreb do
|
111
|
+
subject.morning do
|
112
|
+
subject.coal_error '8t'
|
113
|
+
subject.sugar '9t'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end.to raise_error(NonExistantDimensionAttribute, 'Nonexistan dimension attribute :coal_error')
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should complain if same dimension name exists' do
|
121
|
+
subject.extend(MultidimensionalTable)
|
122
|
+
expect do
|
123
|
+
subject.dimensions = { :material => [:potassium, :coal, :sugar],
|
124
|
+
:city => [:zagreb, :zadar],
|
125
|
+
:time_of_day => [:coal, :zagreb] }
|
126
|
+
end.to raise_error(NonValidDimension, 'Multiple definitions are not allowed : coal for dimension material and time_of_day, zagreb for dimension city and time_of_day')
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: multidimensional_table
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- drKreso
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-25 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: Describe a multidimensional table in pure Ruby
|
15
|
+
email:
|
16
|
+
- kresimir.bojcic@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- .rspec
|
23
|
+
- Gemfile
|
24
|
+
- LICENSE
|
25
|
+
- README.md
|
26
|
+
- Rakefile
|
27
|
+
- lib/multidimensional_table.rb
|
28
|
+
- lib/multidimensional_table/multidimensional_table.rb
|
29
|
+
- lib/multidimensional_table/non_existant_dimension_attribute.rb
|
30
|
+
- lib/multidimensional_table/non_valid_dimension.rb
|
31
|
+
- lib/multidimensional_table/version.rb
|
32
|
+
- multidimensional_table.gemspec
|
33
|
+
- spec/multidimensional_table/multidimensional_table_spec.rb
|
34
|
+
homepage: ''
|
35
|
+
licenses: []
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options: []
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 1.8.17
|
55
|
+
signing_key:
|
56
|
+
specification_version: 3
|
57
|
+
summary: Attempt to make generic API for describing multidimensional data in Ruby
|
58
|
+
test_files:
|
59
|
+
- spec/multidimensional_table/multidimensional_table_spec.rb
|