rubadana 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0a631bf26519d087f84658df366b3e8215ac07d8
4
+ data.tar.gz: c3e5b87d59a2e137288e54bea5c6f488f23d6f98
5
+ SHA512:
6
+ metadata.gz: ce23a984a846411b53048cfd7288082a3431ea4f653fb98e308e79b9a2f55ddc4914b3538b31cdc7cf02437269c1b455af784d8674bf726af519417881b79e28
7
+ data.tar.gz: fd437f3e95811f6f78746b093152aa02c13b35745a1c983dce38cd5f9e8333360132fe62188e0daee13c665ba3cb0b9e56a08d6f38f6d312cdfc6dbd4e1f581b
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ *.gem
2
+ /.bundle/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ mkmf.log
16
+ .#*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubadana.gemspec
4
+ gemspec
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 conanite
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,75 @@
1
+ # Rubadana
2
+
3
+ Rubadana is an elementary ruby data-analysis package. It works with plain old ruby objects, not sql or databases or anything fancy like that.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'rubadana'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install rubadana
20
+
21
+ ## Usage
22
+
23
+ See spec for some examples. The basic idea:
24
+
25
+ 1. Create a Registry for your `Dimension` and `Accumulator` instances
26
+
27
+ my_registry = Rubadana::Registry.new
28
+
29
+ 2. Create and register some `Dimension` instances:
30
+
31
+ ```ruby
32
+ class SaleYear < Rubadana::Dimension
33
+ def name ; "yearly" ; end
34
+ def group_value_for thing ; thing.date.year ; end
35
+ def value_label_for value ; value ; end
36
+ end
37
+
38
+ my_registry.register_dimension SaleYear.new
39
+ ```
40
+
41
+ 3. Create and register some `Accumulator` instances:
42
+
43
+ ```ruby
44
+ class SumSaleAmount < Rubadana::Summation
45
+ def name ; "sum-sale-amount" ; end
46
+ def value_for thing ; thing.sale_amount ; end
47
+ end
48
+
49
+ my_registry.register_accumulator SumSaleAmount.new
50
+ ```
51
+
52
+ 4. Build an analysis program and run it:
53
+
54
+ ```ruby
55
+ # this is a program to analyse invoices by year and product, giving the
56
+ # number of sales, the sum of sales and the average sale in each case
57
+ my_program = register.build ["yearly", "invoice-product"], ["count", "sum-sale-amount", "avg-sale-amount"]
58
+
59
+ data = my_program.run(invoices)
60
+ ```
61
+
62
+ `#run` returns an array of `DataSet` each with the following attributes:
63
+
64
+ * `analyser` - a `Dimension` instance
65
+ * `group_value` - the common value of this dimension for all objects in this data-set
66
+ * `data` - either an accumulated value given by an accumulator, or a nested array of `DataSet` instances
67
+
68
+
69
+ ## Contributing
70
+
71
+ 1. Fork it ( https://github.com/[my-github-username]/rubadana/fork )
72
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
73
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
74
+ 4. Push to the branch (`git push origin my-new-feature`)
75
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,3 @@
1
+ module Rubadana
2
+ VERSION = "0.0.1"
3
+ end
data/lib/rubadana.rb ADDED
@@ -0,0 +1,77 @@
1
+ require "rubadana/version"
2
+
3
+ module Rubadana
4
+ class Registry
5
+ def initialize
6
+ @dimensions = Hash.new
7
+ @accumulators = Hash.new
8
+ end
9
+
10
+ def register_dimension d ; @dimensions[d.name.to_sym] = d ; end
11
+ def register_accumulator a ; @accumulators[a.name.to_sym] = a ; end
12
+ def dimensions ; @dimensions.values ; end
13
+ def accumulators ; @accumulators.values ; end
14
+ def not_nil attr, hsh, name ; hsh[name.to_sym] || raise("unknown #{attr} #{name.inspect}") ; end
15
+ def dimension name ; not_nil "dimension" , @dimensions , name ; end
16
+ def accumulator name ; not_nil "accumulator", @accumulators, name ; end
17
+
18
+ def build dnames, anames
19
+ dd = dnames.compact.map { |n| dimension n }
20
+ aa = anames.compact.map { |n| accumulator n }
21
+ Program.new(dd + aa)
22
+ end
23
+ end
24
+
25
+ class DataSet < Aduki::Initializable
26
+ attr_accessor :analyser, :group_value, :data
27
+ def value_label ; analyser.value_label_for group_value ; end
28
+ end
29
+
30
+ class Accumulator
31
+ def name ; raise "implement this and return a unique name for this accumulator" ; end
32
+ def accumulate things ; raise "implement this and return a value extracted from #things" ; end
33
+ def run things, after ; [DataSet.new(analyser: self, data: accumulate(things))] + after.run(things) ; end
34
+ end
35
+
36
+
37
+ class Summation < Accumulator
38
+ def value_for thing ; raise "implement this and return a value extracted from #thing" ; end
39
+ def accumulate things ; things.map { |thing| value_for(thing) }.reduce :+ ; end
40
+ end
41
+
42
+ class Counter < Accumulator
43
+ def name ; "count" ; end
44
+ def accumulate things ; things.uniq.count ; end
45
+ end
46
+
47
+ class Average < Summation
48
+ def accumulate things ; super / (1.0 * things.count) ; end
49
+ end
50
+
51
+ class Dimension
52
+ def name ; raise "implement this and return a unique name for this dimension" ; end
53
+ def group_value_for thing ; raise "implement this and return a value extracted from #thing" ; end
54
+ def value_label_for value ; raise "implement this to return a display value for #{value.inspect}" ; end
55
+
56
+ def run objects, after
57
+ objects.group_by { |obj| group_value_for obj }.map { |value, list|
58
+ DataSet.new analyser: self, group_value: value, data: after.run(list)
59
+ }
60
+ end
61
+ end
62
+
63
+ class Program
64
+ attr_accessor :dimension, :after
65
+
66
+ def initialize dimensions
67
+ if dimensions
68
+ self.dimension = dimensions.first
69
+ self.after = Program.new dimensions[1..-1]
70
+ end
71
+ end
72
+
73
+ def run objects
74
+ dimension ? dimension.run(objects, after) : []
75
+ end
76
+ end
77
+ end
data/rubadana.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rubadana/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rubadana"
8
+ spec.version = Rubadana::VERSION
9
+ spec.authors = ["Conan Dalton"]
10
+ spec.email = ["conan@conandalton.net"]
11
+ spec.summary = %q{ Simple data grouping and calculations }
12
+ spec.description = %q{ Simple data grouping and calculations. Bring your own extractors. }
13
+ spec.homepage = "http://github.com/conanite/rubadana"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "aduki", "~> 0.2.3"
22
+ spec.add_development_dependency "bundler", "~> 1.7"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency 'rspec'
25
+ end
@@ -0,0 +1,220 @@
1
+ require "spec_helper"
2
+
3
+ describe "analyse invoices" do
4
+ def date str ; Date.parse str ; end
5
+
6
+ class Invoice < Aduki::Initializable
7
+ attr_accessor :type, :date, :amount
8
+ end
9
+
10
+ class InvoiceMonth < Rubadana::Dimension
11
+ def name ; "monthly" ; end
12
+ def group_value_for thing ; Date.new(thing.date.year, thing.date.month, 1) ; end # rails just use #beginning_of_month
13
+ def value_label_for value ; value.strftime "%B %Y" ; end # better with I18n
14
+ end
15
+
16
+ class InvoiceYear < Rubadana::Dimension
17
+ def name ; "yearly" ; end
18
+ def group_value_for thing ; thing.date.year ; end
19
+ def value_label_for value ; value ; end
20
+ end
21
+
22
+ class InvoiceType < Rubadana::Dimension
23
+ def name ; "type" ; end
24
+ def group_value_for thing ; thing.type ; end
25
+ def value_label_for value ; value.to_s ; end
26
+ end
27
+
28
+ class InvoiceScale < Rubadana::Dimension
29
+ def name ; "scale" ; end
30
+ def group_value_for thing ; Math.log(thing.amount, 10).to_i ; end
31
+ def value_label_for value ; value ; end
32
+ end
33
+
34
+ class InvoiceSum < Rubadana::Summation
35
+ def name ; "sum-amount" ; end
36
+ def value_for thing ; thing.amount ; end
37
+ end
38
+
39
+ class InvoiceAvg < Rubadana::Average
40
+ def name ; "avg-amount" ; end
41
+ def value_for thing ; thing.amount ; end
42
+ end
43
+
44
+ let(:i00) { Invoice.new type: "SalesInvoice" , date: date("2020-02-01"), amount: 53 }
45
+ let(:i01) { Invoice.new type: "PurchaseInvoice" , date: date("2021-04-02"), amount: 1100 }
46
+ let(:i02) { Invoice.new type: "SalesCreditNote" , date: date("2020-02-03"), amount: 23000 }
47
+ let(:i03) { Invoice.new type: "SalesCreditNote" , date: date("2021-04-04"), amount: 3100 }
48
+ let(:i04) { Invoice.new type: "Quote" , date: date("2020-05-05"), amount: 43000 }
49
+ let(:i05) { Invoice.new type: "Order" , date: date("2021-12-06"), amount: 59 }
50
+ let(:i06) { Invoice.new type: "SalesInvoice" , date: date("2020-02-07"), amount: 6100 }
51
+ let(:i07) { Invoice.new type: "PurchaseInvoice" , date: date("2022-06-08"), amount: 79000 }
52
+ let(:i08) { Invoice.new type: "PurchaseCreditNote", date: date("2020-05-09"), amount: 83000 }
53
+ let(:i09) { Invoice.new type: "SalesInvoice" , date: date("2020-05-10"), amount: 990 }
54
+ let(:i10) { Invoice.new type: "SalesInvoice" , date: date("2022-06-11"), amount: 130 }
55
+ let(:i11) { Invoice.new type: "PurchaseInvoice" , date: date("2022-12-12"), amount: 1700 }
56
+ let(:i12) { Invoice.new type: "SalesInvoice" , date: date("2020-11-13"), amount: 19000 }
57
+ let(:i13) { Invoice.new type: "PurchaseCreditNote", date: date("2020-11-14"), amount: 23 }
58
+ let(:i14) { Invoice.new type: "SalesInvoice" , date: date("2021-04-15"), amount: 110 }
59
+ let(:i15) { Invoice.new type: "SalesInvoice" , date: date("2022-06-16"), amount: 170000 }
60
+
61
+ let(:invoices) { [ i00,i01,i02,i03,i04,i05,i06,i07,i08,i09,i10,i11,i12,i13,i14,i15 ]}
62
+ let(:register) { Rubadana::Registry.new }
63
+
64
+ before {
65
+ register.register_dimension InvoiceYear.new
66
+ register.register_dimension InvoiceMonth.new
67
+ register.register_dimension InvoiceType.new
68
+ register.register_dimension InvoiceScale.new
69
+ register.register_accumulator InvoiceSum.new
70
+ register.register_accumulator InvoiceAvg.new
71
+ register.register_accumulator Rubadana::Counter.new
72
+ }
73
+
74
+ it "groups items by month and counts them" do
75
+ program = register.build ["monthly"], ["count"]
76
+ data = program.run(invoices)
77
+ actual = data.sort_by(&:group_value).map { |d| [d.value_label] + d.data.map(&:data) }
78
+ expected = [
79
+ ["February 2020", 3],
80
+ ["May 2020" , 3],
81
+ ["November 2020", 2],
82
+ ["April 2021" , 3],
83
+ ["December 2021", 1],
84
+ ["June 2022" , 3],
85
+ ["December 2022", 1],
86
+ ]
87
+ expect(actual).to eq expected
88
+ end
89
+
90
+ it "groups items by year and sums them" do
91
+ program = register.build ["yearly"], ["sum-amount"]
92
+ data = program.run(invoices)
93
+ actual = data.sort_by(&:group_value).map { |d| [d.value_label] + d.data.map(&:data) }
94
+ expected = [
95
+ [2020, 175166],
96
+ [2021, 4369],
97
+ [2022, 250830],
98
+ ]
99
+ expect(actual).to eq expected
100
+ end
101
+
102
+ it "groups items by year and counts them" do
103
+ program = register.build ["yearly"], ["count"]
104
+ data = program.run(invoices)
105
+ actual = data.sort_by(&:group_value).map { |d| [d.value_label] + d.data.map(&:data) }
106
+ expected = [
107
+ [2020, 8],
108
+ [2021, 4],
109
+ [2022, 4],
110
+ ]
111
+ expect(actual).to eq expected
112
+ end
113
+
114
+ it "groups items by year and averages them" do
115
+ program = register.build ["yearly"], ["avg-amount"]
116
+ data = program.run(invoices)
117
+ actual = data.sort_by(&:group_value).map { |d| [d.value_label] + d.data.map(&:data) }
118
+ expected = [
119
+ [2020, 21895.75],
120
+ [2021, 1092.25],
121
+ [2022, 62707.5 ],
122
+ ]
123
+ expect(actual).to eq expected
124
+ end
125
+
126
+ it "groups items by year and gives the count, sum, and average" do
127
+ program = register.build ["yearly"], ["count", "sum-amount", "avg-amount"]
128
+ data = program.run(invoices)
129
+ actual = data.sort_by(&:group_value).map { |d| [d.value_label] + d.data.map(&:data) }
130
+ expected = [
131
+ [2020, 8, 175166, 21895.75],
132
+ [2021, 4, 4369, 1092.25],
133
+ [2022, 4, 250830, 62707.5 ],
134
+ ]
135
+ expect(actual).to eq expected
136
+ end
137
+
138
+ it "groups items by year and by type and by scale and counts them" do
139
+ program = register.build ["yearly", "type"], ["count"]
140
+ data = program.run(invoices)
141
+ actual = data.sort_by(&:group_value).inject([]) { |arr, d|
142
+ d.data.sort_by(&:group_value).each { |s|
143
+ arr << [d.value_label, s.value_label] + s.data.map(&:data) }
144
+ arr
145
+ }
146
+
147
+ expected = [
148
+ [2020 , "PurchaseCreditNote" , 2 ],
149
+ [2020 , "Quote" , 1 ],
150
+ [2020 , "SalesCreditNote" , 1 ],
151
+ [2020 , "SalesInvoice" , 4 ],
152
+ [2021 , "Order" , 1 ],
153
+ [2021 , "PurchaseInvoice" , 1 ],
154
+ [2021 , "SalesCreditNote" , 1 ],
155
+ [2021 , "SalesInvoice" , 1 ],
156
+ [2022 , "PurchaseInvoice" , 2 ],
157
+ [2022 , "SalesInvoice" , 2 ],
158
+ ]
159
+ expect(actual).to eq expected
160
+ end
161
+
162
+ it "groups items by scale and by type and sums them" do
163
+ program = register.build ["scale", "type"], ["sum-amount"]
164
+ data = program.run(invoices)
165
+ actual = data.sort_by(&:group_value).inject([]) { |arr, d|
166
+ d.data.sort_by(&:group_value).each { |s|
167
+ arr << [d.value_label, s.value_label] + s.data.map(&:data) }
168
+ arr
169
+ }
170
+
171
+ expected = [
172
+ [1 , "Order" , 59 ] ,
173
+ [1 , "PurchaseCreditNote" , 23 ] ,
174
+ [1 , "SalesInvoice" , 53 ] ,
175
+ [2 , "SalesInvoice" , 1230 ] ,
176
+ [3 , "PurchaseInvoice" , 2800 ] ,
177
+ [3 , "SalesCreditNote" , 3100 ] ,
178
+ [3 , "SalesInvoice" , 6100 ] ,
179
+ [4 , "PurchaseCreditNote" , 83000 ] ,
180
+ [4 , "PurchaseInvoice" , 79000 ] ,
181
+ [4 , "Quote" , 43000 ] ,
182
+ [4 , "SalesCreditNote" , 23000 ] ,
183
+ [4 , "SalesInvoice" , 19000 ] ,
184
+ [5 , "SalesInvoice" , 170000 ] ]
185
+
186
+ expect(actual).to eq expected
187
+ end
188
+
189
+ it "groups items by year and by type and by scale and counts them" do
190
+ program = register.build ["yearly", "type", "scale"], ["sum-amount"]
191
+ data = program.run(invoices)
192
+ actual = data.sort_by(&:group_value).inject([]) { |arr, d|
193
+ d.data.sort_by(&:group_value).each { |s|
194
+ s.data.sort_by(&:group_value).each { |z|
195
+ arr << [d.value_label, s.value_label, z.value_label] + z.data.map(&:data) }
196
+ }
197
+ arr
198
+ }
199
+
200
+ expected = [
201
+ [2020 , "PurchaseCreditNote" , 1 , 23.0 ],
202
+ [2020 , "PurchaseCreditNote" , 4 , 83000.0 ],
203
+ [2020 , "Quote" , 4 , 43000.0 ],
204
+ [2020 , "SalesCreditNote" , 4 , 23000.0 ],
205
+ [2020 , "SalesInvoice" , 1 , 53.0 ],
206
+ [2020 , "SalesInvoice" , 2 , 990.0 ],
207
+ [2020 , "SalesInvoice" , 3 , 6100.0 ],
208
+ [2020 , "SalesInvoice" , 4 , 19000.0 ],
209
+ [2021 , "Order" , 1 , 59.0 ],
210
+ [2021 , "PurchaseInvoice" , 3 , 1100.0 ],
211
+ [2021 , "SalesCreditNote" , 3 , 3100.0 ],
212
+ [2021 , "SalesInvoice" , 2 , 110.0 ],
213
+ [2022 , "PurchaseInvoice" , 3 , 1700.0 ],
214
+ [2022 , "PurchaseInvoice" , 4 , 79000.0 ],
215
+ [2022 , "SalesInvoice" , 2 , 130.0 ],
216
+ [2022 , "SalesInvoice" , 5 , 170000.0 ]
217
+ ]
218
+ expect(actual).to eq expected
219
+ end
220
+ end
@@ -0,0 +1,19 @@
1
+ require 'aduki'
2
+ require 'rubadana'
3
+
4
+ # This file was generated by the `rspec --init` command. Conventionally, all
5
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
6
+ # Require this file using `require "spec_helper"` to ensure that it is only
7
+ # loaded once.
8
+ #
9
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
10
+ RSpec.configure do |config|
11
+ config.run_all_when_everything_filtered = true
12
+ config.filter_run :focus
13
+
14
+ # Run specs in random order to surface order dependencies. If you find an
15
+ # order dependency and want to debug it, you can fix the order by providing
16
+ # the seed, which is printed after each run.
17
+ # --seed 1234
18
+ config.order = 'random'
19
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubadana
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Conan Dalton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aduki
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: " Simple data grouping and calculations. Bring your own extractors. "
70
+ email:
71
+ - conan@conandalton.net
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - Gemfile
79
+ - MIT-LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - lib/rubadana.rb
83
+ - lib/rubadana/version.rb
84
+ - rubadana.gemspec
85
+ - spec/analyse_invoices_spec.rb
86
+ - spec/spec_helper.rb
87
+ homepage: http://github.com/conanite/rubadana
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.2.2
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Simple data grouping and calculations
111
+ test_files:
112
+ - spec/analyse_invoices_spec.rb
113
+ - spec/spec_helper.rb