enzymator 0.0.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +0 -6
- data/LICENSE +21 -0
- data/README.md +4 -4
- data/Rakefile +1 -1
- data/enzymator.gemspec +8 -2
- data/lib/enzymator.rb +28 -2
- data/lib/enzymator/aggregations/average.rb +30 -0
- data/lib/enzymator/aggregations/base.rb +15 -0
- data/lib/enzymator/aggregations/count.rb +18 -0
- data/lib/enzymator/aggregations/mapreduce.rb +31 -0
- data/lib/enzymator/aggregations/merge_hashes.rb +14 -0
- data/lib/enzymator/aggregations/sum.rb +14 -0
- data/lib/enzymator/core_ext/categorizer.rb +23 -0
- data/lib/enzymator/core_ext/monoids.rb +76 -0
- data/lib/enzymator/transformations/base.rb +39 -0
- data/lib/enzymator/transformations/config.rb +22 -0
- data/lib/enzymator/transformations/foldable/reduction.rb +22 -0
- data/lib/enzymator/transformations/functor/mapping.rb +19 -0
- data/lib/enzymator/transformations/monad/join.rb +15 -0
- data/lib/enzymator/transformations/object/application.rb +15 -0
- data/lib/enzymator/transformations/object/pipelining.rb +19 -0
- data/lib/enzymator/transformers/base.rb +11 -0
- data/lib/enzymator/transformers/foldable/simple_reducer.rb +13 -0
- data/lib/enzymator/transformers/functor/simple_mapper.rb +13 -0
- data/lib/enzymator/transformers/monad/simple_joiner.rb +13 -0
- data/lib/enzymator/transformers/object/simple_applicator.rb +13 -0
- data/lib/enzymator/transformers/object/simple_pipeliner.rb +13 -0
- data/lib/enzymator/types/list/foldable/enumerable.rb +33 -0
- data/lib/enzymator/types/list/functor/enumerable.rb +13 -0
- data/lib/enzymator/types/list/monad/array.rb +17 -0
- data/lib/enzymator/version.rb +2 -3
- data/test/aggregations/test_average.rb +15 -0
- data/test/aggregations/test_count.rb +15 -0
- data/test/aggregations/test_merge_hashes.rb +15 -0
- data/test/aggregations/test_sum.rb +15 -0
- data/test/{test_aggregation.rb → test_introduction.rb} +12 -17
- data/test/transformations/test_application.rb +17 -0
- data/test/transformations/test_join.rb +14 -0
- data/test/transformations/test_mapping.rb +16 -0
- data/test/transformations/test_reduction.rb +69 -0
- metadata +92 -11
- data/Gemfile.lock +0 -33
- data/lib/enzymator/aggregation.rb +0 -63
- data/lib/enzymator/core_ext/enumerator.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db6b848beb8684576018917cc3294e62ee74c9b9
|
4
|
+
data.tar.gz: 13464a8ce27e97366cdd446ade0f40ef1d9c96fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06c96d05b1719d9b82edfd21dcfef75f96a77123f7d2b20fb934f7659a012f1a60ed975d0f891042a2fec58d322ce831b4fa40b2a00cc5cf21aa42612237c5bf
|
7
|
+
data.tar.gz: 87eb91c6b2c7ec3afb559e8fcd450bb93e3177c78259a120ebcc83f0498a5588fec36048d547779df0953e2f97b3a96bccf68406622afda5ce2c5b693280dfc4
|
data/Gemfile
CHANGED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 eugeniobruno
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Enzymator
|
2
2
|
|
3
|
-
|
3
|
+
Enzymator is an extremely simple and powerful aggregation framework. Every ruby app can use it to unleash the infinite power of the MapReduce concept. If it could tame BigData, imagine what it can do with fewer data!
|
4
4
|
|
5
5
|
You should take two minutes to check out the test examples.
|
6
6
|
|
@@ -8,7 +8,7 @@ You should take two minutes to check out the test examples.
|
|
8
8
|
|
9
9
|
Add this line to your application's Gemfile:
|
10
10
|
|
11
|
-
gem '
|
11
|
+
gem 'enzymator'
|
12
12
|
|
13
13
|
And then execute:
|
14
14
|
|
@@ -16,11 +16,11 @@ And then execute:
|
|
16
16
|
|
17
17
|
Or install it yourself as:
|
18
18
|
|
19
|
-
$ gem install
|
19
|
+
$ gem install enzymator
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
You set up a new
|
23
|
+
You set up a new transformation instance, tell it to run with your Object, and get the result. The tests are self-explanatory.
|
24
24
|
|
25
25
|
## Contributing
|
26
26
|
|
data/Rakefile
CHANGED
data/enzymator.gemspec
CHANGED
@@ -6,9 +6,9 @@ require 'enzymator/version'
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = 'enzymator'
|
8
8
|
s.version = Enzymator::Version
|
9
|
-
s.date = '2016-07-
|
9
|
+
s.date = '2016-07-24'
|
10
10
|
s.summary = "An extremely simple and powerful aggregation framework"
|
11
|
-
s.description = "A gem to perform any kind of
|
11
|
+
s.description = "A gem to perform any kind of transformation on any kind of data. The essence of MapReduce distilled in a few lines of code for the ruby community to enjoy."
|
12
12
|
s.authors = ["Eugenio Bruno"]
|
13
13
|
s.email = 'eugeniobruno@gmail.com'
|
14
14
|
s.homepage = 'http://rubygems.org/gems/enzymator'
|
@@ -19,4 +19,10 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
20
20
|
s.require_paths = ['lib']
|
21
21
|
|
22
|
+
s.required_ruby_version = '>= 2.0.0'
|
23
|
+
|
24
|
+
s.add_development_dependency 'rake', ['~> 11.2']
|
25
|
+
s.add_development_dependency 'minitest', ['~> 5.9']
|
26
|
+
s.add_development_dependency 'pry-byebug', ['~> 3.4']
|
27
|
+
|
22
28
|
end
|
data/lib/enzymator.rb
CHANGED
@@ -1,2 +1,28 @@
|
|
1
|
-
require 'enzymator/core_ext/
|
2
|
-
require 'enzymator/
|
1
|
+
require 'enzymator/core_ext/categorizer'
|
2
|
+
require 'enzymator/core_ext/monoids'
|
3
|
+
|
4
|
+
require 'enzymator/types/list/functor/enumerable'
|
5
|
+
require 'enzymator/types/list/foldable/enumerable'
|
6
|
+
require 'enzymator/types/list/monad/array'
|
7
|
+
|
8
|
+
require 'enzymator/transformations/config'
|
9
|
+
require 'enzymator/transformations/base'
|
10
|
+
require 'enzymator/transformations/object/application'
|
11
|
+
require 'enzymator/transformations/object/pipelining'
|
12
|
+
require 'enzymator/transformations/functor/mapping'
|
13
|
+
require 'enzymator/transformations/foldable/reduction'
|
14
|
+
require 'enzymator/transformations/monad/join'
|
15
|
+
|
16
|
+
require 'enzymator/transformers/base'
|
17
|
+
require 'enzymator/transformers/object/simple_applicator'
|
18
|
+
require 'enzymator/transformers/object/simple_pipeliner'
|
19
|
+
require 'enzymator/transformers/functor/simple_mapper'
|
20
|
+
require 'enzymator/transformers/foldable/simple_reducer'
|
21
|
+
require 'enzymator/transformers/monad/simple_joiner'
|
22
|
+
|
23
|
+
require 'enzymator/aggregations/base'
|
24
|
+
require 'enzymator/aggregations/count'
|
25
|
+
require 'enzymator/aggregations/sum'
|
26
|
+
require 'enzymator/aggregations/average'
|
27
|
+
require 'enzymator/aggregations/merge_hashes'
|
28
|
+
require 'enzymator/aggregations/mapreduce'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Enzymator
|
2
|
+
module Aggregations
|
3
|
+
class Average < Base
|
4
|
+
|
5
|
+
def initialize(config = {})
|
6
|
+
super
|
7
|
+
|
8
|
+
@transformation = begin
|
9
|
+
stages = []
|
10
|
+
|
11
|
+
stages << Enzymator::Transformations::Foldable::Reduction.new({
|
12
|
+
append: ->(acum, n) { acum[:sum] += n; acum[:count] += 1; acum },
|
13
|
+
empty: Hash.new(0)
|
14
|
+
})
|
15
|
+
|
16
|
+
stages << Enzymator::Transformations::Object::Application.new({
|
17
|
+
function: ->(sum_and_count) { sum_and_count[:sum] / sum_and_count[:count] }
|
18
|
+
})
|
19
|
+
|
20
|
+
Enzymator::Transformations::Object::Pipelining.new({
|
21
|
+
stages: stages
|
22
|
+
})
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Enzymator
|
2
|
+
module Aggregations
|
3
|
+
class Count < Base
|
4
|
+
|
5
|
+
def initialize(config = {})
|
6
|
+
super
|
7
|
+
|
8
|
+
@transformation = begin
|
9
|
+
Enzymator::Transformations::Object::Application.new({
|
10
|
+
function: ->(objects) { objects.size }
|
11
|
+
})
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Enzymator
|
2
|
+
module Aggregations
|
3
|
+
class MapReduce < Base
|
4
|
+
|
5
|
+
def initialize(config = {})
|
6
|
+
super
|
7
|
+
|
8
|
+
@transformation = begin
|
9
|
+
stages = []
|
10
|
+
|
11
|
+
if @config.map || @config.mapping
|
12
|
+
stages << Enzymator::Transformations::Functor::Mapping.new({
|
13
|
+
map: @config.map || @config.mapping
|
14
|
+
})
|
15
|
+
end
|
16
|
+
|
17
|
+
stages << Enzymator::Transformations::Foldable::Reduction.new({
|
18
|
+
append: @config.append || @config.reduction,
|
19
|
+
empty: @config.empty
|
20
|
+
})
|
21
|
+
|
22
|
+
Enzymator::Transformations::Object::Pipelining.new({
|
23
|
+
stages: stages
|
24
|
+
})
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Enzymator
|
2
|
+
module Aggregations
|
3
|
+
class MergeHashes < Base
|
4
|
+
|
5
|
+
def initialize(config = {})
|
6
|
+
super
|
7
|
+
|
8
|
+
@transformation = Enzymator::Transformations::Foldable::Reduction.new
|
9
|
+
# Merge is the default reduction operation for hashes
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Enzymator
|
2
|
+
module Aggregations
|
3
|
+
class Sum < Base
|
4
|
+
|
5
|
+
def initialize(config = {})
|
6
|
+
super
|
7
|
+
|
8
|
+
@transformation = Enzymator::Transformations::Foldable::Reduction.new
|
9
|
+
# Summation is the default reduction operation for all numeric types
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Enzymator
|
2
|
+
class Categorizer
|
3
|
+
|
4
|
+
@@known_classes = Hash.new(0)
|
5
|
+
|
6
|
+
def self.categorize(klass)
|
7
|
+
unless @@known_classes.has_key? klass
|
8
|
+
|
9
|
+
if klass.included_modules.include? Enumerable
|
10
|
+
klass.send :include, Enzymator::Types::List::Functor::Enumerable
|
11
|
+
klass.send :include, Enzymator::Types::List::Foldable::Enumerable
|
12
|
+
end
|
13
|
+
|
14
|
+
if klass <= Array
|
15
|
+
klass.send :include, Enzymator::Types::List::Monad::Array
|
16
|
+
end
|
17
|
+
|
18
|
+
@@known_classes[klass] += 1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
class Fixnum
|
2
|
+
@enzy_mappend = ->(m, n) { m + n }.freeze
|
3
|
+
@enzy_mempty = 0.freeze
|
4
|
+
class << self
|
5
|
+
attr_reader :enzy_mappend
|
6
|
+
attr_reader :enzy_mempty
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Float
|
11
|
+
@enzy_mappend = ->(m, n) { m + n }.freeze
|
12
|
+
@enzy_mempty = 0.to_f.freeze
|
13
|
+
class << self
|
14
|
+
attr_reader :enzy_mappend
|
15
|
+
attr_reader :enzy_mempty
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Rational
|
20
|
+
@enzy_mappend = ->(m, n) { m + n }.freeze
|
21
|
+
@enzy_mempty = 0.to_r.freeze
|
22
|
+
class << self
|
23
|
+
attr_reader :enzy_mappend
|
24
|
+
attr_reader :enzy_mempty
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Complex
|
29
|
+
@enzy_mappend = ->(m, n) { m + n }.freeze
|
30
|
+
@enzy_mempty = 0.to_c.freeze
|
31
|
+
class << self
|
32
|
+
attr_reader :enzy_mappend
|
33
|
+
attr_reader :enzy_mempty
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Array
|
38
|
+
@enzy_mappend = ->(xs, ys) { xs + ys }.freeze
|
39
|
+
@enzy_mempty = [].freeze
|
40
|
+
class << self
|
41
|
+
attr_reader :enzy_mappend
|
42
|
+
#attr_reader :enzy_mempty
|
43
|
+
def enzy_mempty
|
44
|
+
@enzy_mempty.dup
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class String
|
50
|
+
@enzy_mappend = ->(s, t) { s + t }.freeze
|
51
|
+
@enzy_mempty = ''.freeze
|
52
|
+
class << self
|
53
|
+
attr_reader :enzy_mappend
|
54
|
+
attr_reader :enzy_mempty
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Hash
|
59
|
+
@enzy_mappend = ->(ps, qs) { ps.merge qs }.freeze
|
60
|
+
@enzy_mempty = {}.freeze
|
61
|
+
class << self
|
62
|
+
attr_reader :enzy_mappend
|
63
|
+
attr_reader :enzy_mempty
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
if defined? HashWithIndifferentAccess
|
68
|
+
class HashHashWithIndifferentAccess
|
69
|
+
@enzy_mappend = ->(ps, qs) { ps.merge qs }.freeze
|
70
|
+
@enzy_mempty = {}.freeze
|
71
|
+
class << self
|
72
|
+
attr_reader :enzy_mappend
|
73
|
+
attr_reader :enzy_mempty
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Enzymator
|
2
|
+
module Transformations
|
3
|
+
class Base
|
4
|
+
|
5
|
+
@@default_config = {
|
6
|
+
max_threads: 1 # TODO: use this value
|
7
|
+
}
|
8
|
+
|
9
|
+
def initialize(config = {})
|
10
|
+
@config = Config.new(interpreted(@@default_config.merge(config)))
|
11
|
+
end
|
12
|
+
|
13
|
+
def run_on_all(*objects)
|
14
|
+
# when threads are implemented, the original order must be preserved
|
15
|
+
objects.map { |object| run_on(object) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def run_on(object)
|
19
|
+
Categorizer.categorize(object.class)
|
20
|
+
do_run_on(object)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def interpreted(config)
|
26
|
+
config
|
27
|
+
end
|
28
|
+
|
29
|
+
def do_run_on(object)
|
30
|
+
transformer.transform(object)
|
31
|
+
end
|
32
|
+
|
33
|
+
def transformer
|
34
|
+
transformer_class.new(@config)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Enzymator
|
2
|
+
module Transformations
|
3
|
+
class Config
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@config = config.each_with_object({}) do |(k, v), hash|
|
7
|
+
hash[k.to_sym] = v
|
8
|
+
end
|
9
|
+
.freeze
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(name, *args, &block)
|
13
|
+
@config[name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def respond_to_missing?(name, include_private = false)
|
17
|
+
@config.has_key?(name) || super
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Enzymator
|
2
|
+
module Transformations
|
3
|
+
module Foldable
|
4
|
+
class Reduction < Base
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def transformer_class
|
9
|
+
Enzymator::Transformers::Foldable::SimpleReducer
|
10
|
+
end
|
11
|
+
|
12
|
+
def interpreted(config)
|
13
|
+
config.merge({
|
14
|
+
append: config[:append] || config[:reduction] || :mappend,
|
15
|
+
empty: config[:empty] || config[:initial] || :mempty
|
16
|
+
})
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Enzymator
|
2
|
+
module Transformations
|
3
|
+
module Functor
|
4
|
+
class Mapping < Base
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def transformer_class
|
9
|
+
Enzymator::Transformers::Functor::SimpleMapper
|
10
|
+
end
|
11
|
+
|
12
|
+
def interpreted(config)
|
13
|
+
config.merge({ map: config[:map] || config[:mapping] })
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Enzymator
|
2
|
+
module Transformations
|
3
|
+
module Object
|
4
|
+
class Pipelining < Base
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def transformer_class
|
9
|
+
Enzymator::Transformers::Object::SimplePipeliner
|
10
|
+
end
|
11
|
+
|
12
|
+
def interpreted(config)
|
13
|
+
config.merge({ stages: config[:stages] || config[:transformations] || [] })
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Enzymator
|
2
|
+
module Types
|
3
|
+
module List
|
4
|
+
module Foldable
|
5
|
+
module Enumerable
|
6
|
+
|
7
|
+
def enzy_fold(empty, append)
|
8
|
+
append = enzy_mappend if append == :mappend
|
9
|
+
empty = enzy_mempty if empty == :mempty
|
10
|
+
|
11
|
+
reduce(empty, &append)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def enzy_mappend
|
17
|
+
enzy_inner_type.enzy_mappend
|
18
|
+
end
|
19
|
+
|
20
|
+
def enzy_mempty
|
21
|
+
enzy_inner_type.enzy_mempty
|
22
|
+
end
|
23
|
+
|
24
|
+
def enzy_inner_type
|
25
|
+
first_elem = first
|
26
|
+
first_elem.nil? ? :missing_inner_type : first_elem.class
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/enzymator/version.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'enzymator'
|
3
|
+
|
4
|
+
class TestAverage < Minitest::Test
|
5
|
+
|
6
|
+
def test_average
|
7
|
+
numbers = (1..10).to_a
|
8
|
+
expected = (1..10).reduce(:+) / 10
|
9
|
+
|
10
|
+
actual = Enzymator::Aggregations::Average.new.run_on(numbers)
|
11
|
+
|
12
|
+
assert_equal expected, actual
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'enzymator'
|
3
|
+
|
4
|
+
class TestCount < Minitest::Test
|
5
|
+
|
6
|
+
def test_count
|
7
|
+
numbers = (1..10).to_a
|
8
|
+
expected = numbers.length
|
9
|
+
|
10
|
+
actual = Enzymator::Aggregations::Count.new.run_on(numbers)
|
11
|
+
|
12
|
+
assert_equal expected, actual
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'enzymator'
|
3
|
+
|
4
|
+
class TestMergeHashes < Minitest::Test
|
5
|
+
|
6
|
+
def test_merge_hashes
|
7
|
+
hashes = [ { 'a' => 1, 'b' => 2, 'c' => 3 }, { 'd' => 4 }, { 'e' => 5 } ]
|
8
|
+
expected = { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }
|
9
|
+
|
10
|
+
actual = Enzymator::Aggregations::MergeHashes.new.run_on(hashes)
|
11
|
+
|
12
|
+
assert_equal expected, actual
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'enzymator'
|
3
|
+
|
4
|
+
class TestSum < Minitest::Test
|
5
|
+
|
6
|
+
def test_sum
|
7
|
+
numbers = (1..10).to_a
|
8
|
+
expected = (1..10).reduce(:+)
|
9
|
+
|
10
|
+
actual = Enzymator::Aggregations::Sum.new.run_on(numbers)
|
11
|
+
|
12
|
+
assert_equal expected, actual
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
2
|
require 'enzymator'
|
3
3
|
|
4
|
-
class
|
4
|
+
class TestIntroduction < Minitest::Test
|
5
5
|
|
6
6
|
def test_sum_numbers
|
7
7
|
numbers = (1..1000).to_a
|
@@ -9,10 +9,8 @@ class AggregationTest < Minitest::Test
|
|
9
9
|
expected_sum = numbers.reduce(:+)
|
10
10
|
|
11
11
|
# but where's the fun in that...
|
12
|
-
actual_sum = Enzymator::
|
13
|
-
|
14
|
-
map: lambda { |n| n },
|
15
|
-
reduce: lambda { |acum, n, _| acum + n },
|
12
|
+
actual_sum = Enzymator::Aggregations::MapReduce.new({
|
13
|
+
reduction: ->(acum, n) { acum + n }
|
16
14
|
}).run_on(numbers)
|
17
15
|
|
18
16
|
assert_equal expected_sum, actual_sum
|
@@ -39,14 +37,10 @@ class AggregationTest < Minitest::Test
|
|
39
37
|
'fat.' => 1,
|
40
38
|
}
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
}).run_on!(words)
|
47
|
-
# notice the bang
|
48
|
-
|
49
|
-
actual_hash = aggregation.result
|
40
|
+
actual_hash = Enzymator::Aggregations::MapReduce.new({
|
41
|
+
reduction: ->(acum, w) { acum[w] += 1; acum },
|
42
|
+
empty: Hash.new(0)
|
43
|
+
}).run_on(words)
|
50
44
|
|
51
45
|
assert_equal expected_hash, actual_hash
|
52
46
|
|
@@ -55,10 +49,11 @@ class AggregationTest < Minitest::Test
|
|
55
49
|
expected_hash.delete('The')
|
56
50
|
expected_hash = expected_hash.map { |k, v| { k.downcase => v } }.reduce(&:merge)
|
57
51
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
52
|
+
actual_hash = Enzymator::Aggregations::MapReduce.new({
|
53
|
+
mapping: ->(w) { w.downcase },
|
54
|
+
reduction: ->(acum, w) { acum[w] += 1; acum },
|
55
|
+
empty: Hash.new(0)
|
56
|
+
}).run_on(words)
|
62
57
|
|
63
58
|
assert_equal expected_hash, actual_hash
|
64
59
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'enzymator'
|
3
|
+
|
4
|
+
class TestApplication < Minitest::Test
|
5
|
+
|
6
|
+
def test_application
|
7
|
+
object = 26
|
8
|
+
expected = 27
|
9
|
+
|
10
|
+
actual = Enzymator::Transformations::Object::Application.new({
|
11
|
+
function: ->(x) { x + 1 }
|
12
|
+
}).run_on(object)
|
13
|
+
|
14
|
+
assert_equal expected, actual
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'enzymator'
|
3
|
+
|
4
|
+
class TestJoin < Minitest::Test
|
5
|
+
|
6
|
+
def test_join
|
7
|
+
ary = [1, 2, [3, 4], [5], [6, [7, 8], 9]]
|
8
|
+
expected = [1, 2, 3, 4, 5, 6, [7, 8], 9]
|
9
|
+
actual = Enzymator::Transformations::Monad::Join.new.run_on(ary)
|
10
|
+
|
11
|
+
assert_equal expected, actual
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'enzymator'
|
3
|
+
|
4
|
+
class TestMapping < Minitest::Test
|
5
|
+
|
6
|
+
def test_map
|
7
|
+
ary = (1..9).to_a
|
8
|
+
expected = ary.map { |n| n + 1 }
|
9
|
+
actual = Enzymator::Transformations::Functor::Mapping.new({
|
10
|
+
map: ->(n) { n + 1 }
|
11
|
+
}).run_on(ary)
|
12
|
+
|
13
|
+
assert_equal expected, actual
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'enzymator'
|
3
|
+
|
4
|
+
class TestReduction < Minitest::Test
|
5
|
+
|
6
|
+
def test_reduction_sum
|
7
|
+
ary = (1..9).to_a
|
8
|
+
expected = (1..9).to_a.reduce(:+)
|
9
|
+
|
10
|
+
actual_explicit = Enzymator::Transformations::Foldable::Reduction.new({
|
11
|
+
append: ->(acum, n) { acum + n }
|
12
|
+
}).run_on(ary)
|
13
|
+
|
14
|
+
actual_implicit = Enzymator::Transformations::Foldable::Reduction.new
|
15
|
+
.run_on(ary)
|
16
|
+
|
17
|
+
assert_equal expected, actual_explicit
|
18
|
+
assert_equal expected, actual_implicit
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_reduction_product
|
22
|
+
ary = (1..9).to_a
|
23
|
+
expected = (1..9).to_a.reduce(:*)
|
24
|
+
|
25
|
+
absorbed = Enzymator::Transformations::Foldable::Reduction.new({
|
26
|
+
append: ->(acum, n) { acum * n }
|
27
|
+
}).run_on(ary)
|
28
|
+
|
29
|
+
actual = Enzymator::Transformations::Foldable::Reduction.new({
|
30
|
+
append: ->(acum, n) { acum * n },
|
31
|
+
empty: 1
|
32
|
+
}).run_on(ary)
|
33
|
+
|
34
|
+
assert_equal 0, absorbed
|
35
|
+
assert_equal expected, actual
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_reduction_concat
|
39
|
+
ary = (1..9).to_a.map { |n| [+n, -n] }
|
40
|
+
expected = (1..9).to_a.map { |n| [+n, -n] }.reduce(:concat).to_a
|
41
|
+
|
42
|
+
actual_explicit = Enzymator::Transformations::Foldable::Reduction.new({
|
43
|
+
append: ->(acum, pair) { acum + pair },
|
44
|
+
}).run_on(ary)
|
45
|
+
|
46
|
+
actual_implicit = Enzymator::Transformations::Foldable::Reduction.new
|
47
|
+
.run_on(ary)
|
48
|
+
|
49
|
+
assert_equal expected, actual_explicit
|
50
|
+
assert_equal expected, actual_implicit
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_reduction_merge
|
54
|
+
hashes = [ { 'a' => 1, 'b' => 2, 'c' => 3 }, { 'd' => 4 }, { 'e' => 5 } ]
|
55
|
+
expected = { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }
|
56
|
+
|
57
|
+
actual_explicit = Enzymator::Transformations::Foldable::Reduction.new({
|
58
|
+
append: ->(acum, pair) { acum.merge pair },
|
59
|
+
empty: { 'f' => 6 }
|
60
|
+
}).run_on(hashes)
|
61
|
+
|
62
|
+
actual_implicit = Enzymator::Transformations::Foldable::Reduction.new
|
63
|
+
.run_on(hashes)
|
64
|
+
|
65
|
+
assert_equal expected.merge({ 'f' => 6 }), actual_explicit
|
66
|
+
assert_equal expected, actual_implicit
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
metadata
CHANGED
@@ -1,32 +1,105 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: enzymator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eugenio Bruno
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-07-
|
12
|
-
dependencies:
|
13
|
-
|
14
|
-
|
11
|
+
date: 2016-07-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '11.2'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '11.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.9'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry-byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.4'
|
55
|
+
description: A gem to perform any kind of transformation on any kind of data. The
|
56
|
+
essence of MapReduce distilled in a few lines of code for the ruby community to
|
57
|
+
enjoy.
|
15
58
|
email: eugeniobruno@gmail.com
|
16
59
|
executables: []
|
17
60
|
extensions: []
|
18
61
|
extra_rdoc_files: []
|
19
62
|
files:
|
20
63
|
- Gemfile
|
21
|
-
-
|
64
|
+
- LICENSE
|
22
65
|
- README.md
|
23
66
|
- Rakefile
|
24
67
|
- enzymator.gemspec
|
25
68
|
- lib/enzymator.rb
|
26
|
-
- lib/enzymator/
|
27
|
-
- lib/enzymator/
|
69
|
+
- lib/enzymator/aggregations/average.rb
|
70
|
+
- lib/enzymator/aggregations/base.rb
|
71
|
+
- lib/enzymator/aggregations/count.rb
|
72
|
+
- lib/enzymator/aggregations/mapreduce.rb
|
73
|
+
- lib/enzymator/aggregations/merge_hashes.rb
|
74
|
+
- lib/enzymator/aggregations/sum.rb
|
75
|
+
- lib/enzymator/core_ext/categorizer.rb
|
76
|
+
- lib/enzymator/core_ext/monoids.rb
|
77
|
+
- lib/enzymator/transformations/base.rb
|
78
|
+
- lib/enzymator/transformations/config.rb
|
79
|
+
- lib/enzymator/transformations/foldable/reduction.rb
|
80
|
+
- lib/enzymator/transformations/functor/mapping.rb
|
81
|
+
- lib/enzymator/transformations/monad/join.rb
|
82
|
+
- lib/enzymator/transformations/object/application.rb
|
83
|
+
- lib/enzymator/transformations/object/pipelining.rb
|
84
|
+
- lib/enzymator/transformers/base.rb
|
85
|
+
- lib/enzymator/transformers/foldable/simple_reducer.rb
|
86
|
+
- lib/enzymator/transformers/functor/simple_mapper.rb
|
87
|
+
- lib/enzymator/transformers/monad/simple_joiner.rb
|
88
|
+
- lib/enzymator/transformers/object/simple_applicator.rb
|
89
|
+
- lib/enzymator/transformers/object/simple_pipeliner.rb
|
90
|
+
- lib/enzymator/types/list/foldable/enumerable.rb
|
91
|
+
- lib/enzymator/types/list/functor/enumerable.rb
|
92
|
+
- lib/enzymator/types/list/monad/array.rb
|
28
93
|
- lib/enzymator/version.rb
|
29
|
-
- test/
|
94
|
+
- test/aggregations/test_average.rb
|
95
|
+
- test/aggregations/test_count.rb
|
96
|
+
- test/aggregations/test_merge_hashes.rb
|
97
|
+
- test/aggregations/test_sum.rb
|
98
|
+
- test/test_introduction.rb
|
99
|
+
- test/transformations/test_application.rb
|
100
|
+
- test/transformations/test_join.rb
|
101
|
+
- test/transformations/test_mapping.rb
|
102
|
+
- test/transformations/test_reduction.rb
|
30
103
|
homepage: http://rubygems.org/gems/enzymator
|
31
104
|
licenses:
|
32
105
|
- MIT
|
@@ -39,7 +112,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
39
112
|
requirements:
|
40
113
|
- - ">="
|
41
114
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
115
|
+
version: 2.0.0
|
43
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
117
|
requirements:
|
45
118
|
- - ">="
|
@@ -52,4 +125,12 @@ signing_key:
|
|
52
125
|
specification_version: 4
|
53
126
|
summary: An extremely simple and powerful aggregation framework
|
54
127
|
test_files:
|
55
|
-
- test/
|
128
|
+
- test/aggregations/test_average.rb
|
129
|
+
- test/aggregations/test_count.rb
|
130
|
+
- test/aggregations/test_merge_hashes.rb
|
131
|
+
- test/aggregations/test_sum.rb
|
132
|
+
- test/test_introduction.rb
|
133
|
+
- test/transformations/test_application.rb
|
134
|
+
- test/transformations/test_join.rb
|
135
|
+
- test/transformations/test_mapping.rb
|
136
|
+
- test/transformations/test_reduction.rb
|
data/Gemfile.lock
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
enzymator (0.0.1)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: http://rubygems.org/
|
8
|
-
specs:
|
9
|
-
byebug (9.0.5)
|
10
|
-
coderay (1.1.1)
|
11
|
-
method_source (0.8.2)
|
12
|
-
minitest (5.9.0)
|
13
|
-
pry (0.10.4)
|
14
|
-
coderay (~> 1.1.0)
|
15
|
-
method_source (~> 0.8.1)
|
16
|
-
slop (~> 3.4)
|
17
|
-
pry-byebug (3.4.0)
|
18
|
-
byebug (~> 9.0)
|
19
|
-
pry (~> 0.10)
|
20
|
-
rake (11.2.2)
|
21
|
-
slop (3.6.0)
|
22
|
-
|
23
|
-
PLATFORMS
|
24
|
-
ruby
|
25
|
-
|
26
|
-
DEPENDENCIES
|
27
|
-
enzymator!
|
28
|
-
minitest (~> 5.9)
|
29
|
-
pry-byebug
|
30
|
-
rake (~> 11.2)
|
31
|
-
|
32
|
-
BUNDLED WITH
|
33
|
-
1.12.5
|
@@ -1,63 +0,0 @@
|
|
1
|
-
module Enzymator
|
2
|
-
class Aggregation
|
3
|
-
|
4
|
-
@@single_cluster_config = {
|
5
|
-
initial_clusters: lambda { |n| n },
|
6
|
-
enumerator: lambda { |n| [n].each },
|
7
|
-
map_each: lambda { |n| :single_cluster },
|
8
|
-
reduce_each: lambda { |acum, n| :single_cluster },
|
9
|
-
}
|
10
|
-
|
11
|
-
attr_accessor :config, :results
|
12
|
-
|
13
|
-
def initialize(config, results = [])
|
14
|
-
@config = @@single_cluster_config.merge(config)
|
15
|
-
@results = results
|
16
|
-
end
|
17
|
-
|
18
|
-
def last_result
|
19
|
-
results.last
|
20
|
-
end
|
21
|
-
alias :result :last_result
|
22
|
-
|
23
|
-
def patch_config(config_patch)
|
24
|
-
self.config = config.merge(config_patch)
|
25
|
-
self
|
26
|
-
end
|
27
|
-
|
28
|
-
def run_on!(enumerable, options = {}, config = @config)
|
29
|
-
add_result(run_on(enumerable, options, config))
|
30
|
-
self
|
31
|
-
end
|
32
|
-
|
33
|
-
def run_on(enumerable, options = {}, config = @config)
|
34
|
-
config[:initial_clusters].call(enumerable).reduce(config[:null_result].call) do |acum, cluster|
|
35
|
-
group = config[:map].call(cluster)
|
36
|
-
config[:reduce].call( acum, group, aggregate(cluster, config, options) )
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
protected
|
41
|
-
|
42
|
-
def add_result(result)
|
43
|
-
@results.push(result)
|
44
|
-
self
|
45
|
-
end
|
46
|
-
|
47
|
-
def aggregate(cluster, config, options)
|
48
|
-
enum = config[:enumerator].call(cluster)
|
49
|
-
first_element = enum.peek
|
50
|
-
first_map_result = config[:map_each].call(first_element)
|
51
|
-
|
52
|
-
enum.skip(1).reduce(first_map_result) do |acum, e|
|
53
|
-
step(acum, e, config, options)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def step(previous_result, elem, config, options)
|
58
|
-
map_result = config[:map_each].call(elem)
|
59
|
-
config[:reduce_each].call(previous_result, map_result)
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
class Enumerator
|
2
|
-
#
|
3
|
-
# Skip the first n elements and return an Enumerator for the rest, or pass them
|
4
|
-
# in succession to the block, if given.
|
5
|
-
# Extracted from epitools: https://github.com/epitron/epitools
|
6
|
-
#
|
7
|
-
def skip(n)
|
8
|
-
if block_given?
|
9
|
-
each do |x|
|
10
|
-
if n > 0
|
11
|
-
n -= 1
|
12
|
-
else
|
13
|
-
yield x
|
14
|
-
end
|
15
|
-
end
|
16
|
-
else
|
17
|
-
to_enum(:skip, n)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|