multidimensional_table 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.un~
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in multidimensional_table.gemspec
4
+ gemspec
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,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -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,12 @@
1
+ module MultidimensionalTable
2
+ class NonExistantDimensionAttribute < StandardError
3
+ attr_accessor :attribute
4
+ def initialize(attribute)
5
+ @attribute = attribute
6
+ end
7
+
8
+ def to_s
9
+ "Nonexistan dimension attribute :#@attribute"
10
+ end
11
+ end
12
+ 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,3 @@
1
+ module MultidimensionalTable
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,3 @@
1
+ require "multidimensional_table/version"
2
+ require "multidimensional_table/multidimensional_table"
3
+
@@ -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