enzymator 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +33 -0
- data/README.md +31 -0
- data/Rakefile +8 -0
- data/enzymator.gemspec +22 -0
- data/lib/enzymator.rb +2 -0
- data/lib/enzymator/aggregation.rb +63 -0
- data/lib/enzymator/core_ext/enumerator.rb +20 -0
- data/lib/enzymator/version.rb +18 -0
- data/test/test_aggregation.rb +67 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a2ec2a4060b6cda9e9556c05b4feb7f8188b91ed
|
4
|
+
data.tar.gz: 5c01a79eed612fc1c63a7a496695ef5898e7f38f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0d7e5533db3223a545e7c175f3ffe65fa33e3d3e18c7469b1d2dd129a604fd386db91666120d8ea19d14149b7636dcfbe76c57478513bcad707a0d8ad8dc4592
|
7
|
+
data.tar.gz: f7200cc635e0c058d54fa50d8d28593b366f7ea5e61439d4b9c8d81e264b04ffe3a2574c12f79b369af35725707c13643806e85ac9c2c82b47b42ca4a2e25967
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,33 @@
|
|
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
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Enzymator
|
2
|
+
|
3
|
+
Enzimator 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
|
+
|
5
|
+
You should take two minutes to check out the test examples.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'enzimator'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install enzimator
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
You set up a new Aggregation instance, tell it to run with your Enumerator, and get the result. The tests are self-explanatory.
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/enzymator.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'enzymator/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'enzymator'
|
8
|
+
s.version = Enzymator::Version
|
9
|
+
s.date = '2016-07-17'
|
10
|
+
s.summary = "An extremely simple and powerful aggregation framework"
|
11
|
+
s.description = "A gem to perform any kind of calculation on any kind of data. The essence of MapReduce distilled in a few lines of code for the ruby community to enjoy."
|
12
|
+
s.authors = ["Eugenio Bruno"]
|
13
|
+
s.email = 'eugeniobruno@gmail.com'
|
14
|
+
s.homepage = 'http://rubygems.org/gems/enzymator'
|
15
|
+
s.license = 'MIT'
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split($/)
|
18
|
+
s.executables = s.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
19
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
20
|
+
s.require_paths = ['lib']
|
21
|
+
|
22
|
+
end
|
data/lib/enzymator.rb
ADDED
@@ -0,0 +1,63 @@
|
|
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
|
@@ -0,0 +1,20 @@
|
|
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
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'enzymator'
|
3
|
+
|
4
|
+
class AggregationTest < Minitest::Test
|
5
|
+
|
6
|
+
def test_sum_numbers
|
7
|
+
numbers = (1..1000).to_a
|
8
|
+
# to sum the numbers, you can just do
|
9
|
+
expected_sum = numbers.reduce(:+)
|
10
|
+
|
11
|
+
# but where's the fun in that...
|
12
|
+
actual_sum = Enzymator::Aggregation.new({
|
13
|
+
null_result: lambda { 0 },
|
14
|
+
map: lambda { |n| n },
|
15
|
+
reduce: lambda { |acum, n, _| acum + n },
|
16
|
+
}).run_on(numbers)
|
17
|
+
|
18
|
+
assert_equal expected_sum, actual_sum
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_count_words
|
22
|
+
# Let's follow the herd of MapReduce preachers and count some words.
|
23
|
+
words = %w(Hey! Are you the horse from Horsing Around? The word is you got fat.)
|
24
|
+
|
25
|
+
# There is no built-in one-message-one-argument way this time...
|
26
|
+
expected_hash = {
|
27
|
+
'Hey!' => 1,
|
28
|
+
'Are' => 1,
|
29
|
+
'you' => 2,
|
30
|
+
'the' => 1,
|
31
|
+
'horse' => 1,
|
32
|
+
'from' => 1,
|
33
|
+
'Horsing' => 1,
|
34
|
+
'Around?' => 1,
|
35
|
+
'The' => 1,
|
36
|
+
'word' => 1,
|
37
|
+
'is' => 1,
|
38
|
+
'got' => 1,
|
39
|
+
'fat.' => 1,
|
40
|
+
}
|
41
|
+
|
42
|
+
aggregation = Enzymator::Aggregation.new({
|
43
|
+
null_result: lambda { Hash.new(0) },
|
44
|
+
map: lambda { |w| w },
|
45
|
+
reduce: lambda { |acum, w, _| acum[w] += 1; acum }
|
46
|
+
}).run_on!(words)
|
47
|
+
# notice the bang
|
48
|
+
|
49
|
+
actual_hash = aggregation.result
|
50
|
+
|
51
|
+
assert_equal expected_hash, actual_hash
|
52
|
+
|
53
|
+
# I'm thinking 'the' and 'The' should count as one word. Total case-insensitivity
|
54
|
+
expected_hash['the'] = 2
|
55
|
+
expected_hash.delete('The')
|
56
|
+
expected_hash = expected_hash.map { |k, v| { k.downcase => v } }.reduce(&:merge)
|
57
|
+
|
58
|
+
aggregation.patch_config( map: lambda { |w| w.downcase } )
|
59
|
+
.run_on!(words)
|
60
|
+
|
61
|
+
actual_hash = aggregation.result
|
62
|
+
|
63
|
+
assert_equal expected_hash, actual_hash
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: enzymator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eugenio Bruno
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-17 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A gem to perform any kind of calculation on any kind of data. The essence
|
14
|
+
of MapReduce distilled in a few lines of code for the ruby community to enjoy.
|
15
|
+
email: eugeniobruno@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- Gemfile
|
21
|
+
- Gemfile.lock
|
22
|
+
- README.md
|
23
|
+
- Rakefile
|
24
|
+
- enzymator.gemspec
|
25
|
+
- lib/enzymator.rb
|
26
|
+
- lib/enzymator/aggregation.rb
|
27
|
+
- lib/enzymator/core_ext/enumerator.rb
|
28
|
+
- lib/enzymator/version.rb
|
29
|
+
- test/test_aggregation.rb
|
30
|
+
homepage: http://rubygems.org/gems/enzymator
|
31
|
+
licenses:
|
32
|
+
- MIT
|
33
|
+
metadata: {}
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
requirements: []
|
49
|
+
rubyforge_project:
|
50
|
+
rubygems_version: 2.5.1
|
51
|
+
signing_key:
|
52
|
+
specification_version: 4
|
53
|
+
summary: An extremely simple and powerful aggregation framework
|
54
|
+
test_files:
|
55
|
+
- test/test_aggregation.rb
|