aqueductron 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 +7 -0
- data/lib/aqueductron.rb +3 -0
- data/lib/aqueductron/buildering.rb +86 -0
- data/lib/aqueductron/compound_result.rb +19 -0
- data/lib/aqueductron/counting_end_piece.rb +16 -0
- data/lib/aqueductron/duct.rb +22 -0
- data/lib/aqueductron/end_piece.rb +20 -0
- data/lib/aqueductron/inlet.rb +30 -0
- data/lib/aqueductron/joint_piece.rb +32 -0
- data/lib/aqueductron/last_end_piece.rb +16 -0
- data/lib/aqueductron/monoid.rb +20 -0
- data/lib/aqueductron/partition.rb +43 -0
- data/lib/aqueductron/piece.rb +33 -0
- data/lib/aqueductron/piece_common.rb +10 -0
- data/lib/aqueductron/result.rb +7 -0
- data/lib/aqueductron/simple_result.rb +18 -0
- data/lib/improved_hash.rb +6 -0
- metadata +61 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 58506c06a6a555805358ad1545cf71769fc2f3cb
|
4
|
+
data.tar.gz: 0c7d1b25af500789ebb0f91c95bd407349f30eac
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f8da3f12f7657164c0099b5eab8f731086b220b76a2d0b87b9abde68aa1e01a0ff965f7049ff0ae128632160f299e8447a016591f49b047ba426081a41c06883
|
7
|
+
data.tar.gz: 71486cc4c617bfc55ebc6c7f3622abee44ed380a381696007ba3f1143e410ed97519bf78016f5d1793d329c61dd4b66258f1134a748535cd77ffdda1d085c9c5
|
data/lib/aqueductron.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative 'counting_end_piece'
|
2
|
+
require_relative 'last_end_piece'
|
3
|
+
require_relative 'end_piece'
|
4
|
+
require_relative 'inlet'
|
5
|
+
require_relative 'joint_piece'
|
6
|
+
require_relative 'piece'
|
7
|
+
require_relative 'partition'
|
8
|
+
|
9
|
+
module Aqueductron
|
10
|
+
module Buildering
|
11
|
+
def answer(monoid)
|
12
|
+
answer_int(EndPiece.new(monoid))
|
13
|
+
end
|
14
|
+
|
15
|
+
def count
|
16
|
+
answer_int(CountingEndPiece.new)
|
17
|
+
end
|
18
|
+
|
19
|
+
def take(how_many)
|
20
|
+
attach(take_function(how_many))
|
21
|
+
end
|
22
|
+
|
23
|
+
def keeping(predicate)
|
24
|
+
attach(filter_function(predicate))
|
25
|
+
end
|
26
|
+
|
27
|
+
def through(transform)
|
28
|
+
attach(map_function(transform))
|
29
|
+
end
|
30
|
+
|
31
|
+
def custom(piece)
|
32
|
+
attach(piece)
|
33
|
+
end
|
34
|
+
|
35
|
+
def expand(transform)
|
36
|
+
attach(expand_function(transform))
|
37
|
+
end
|
38
|
+
|
39
|
+
def split(paths)
|
40
|
+
answer_int(JointPiece.new(paths))
|
41
|
+
end
|
42
|
+
|
43
|
+
def partition(categorize, make_new_path)
|
44
|
+
answer_int(SpontaneousJointPiece.new({}, categorize, make_new_path))
|
45
|
+
end
|
46
|
+
|
47
|
+
def last
|
48
|
+
answer_int(LastEndPiece.new)
|
49
|
+
end
|
50
|
+
|
51
|
+
module_function
|
52
|
+
def take_function(how_many) # this will either return a Result or a Piece
|
53
|
+
what_to_do = ->(piece, msg) do
|
54
|
+
if (how_many == 0) then # this is a little inefficient. One extra piece of info will be read
|
55
|
+
piece.send_eof
|
56
|
+
else
|
57
|
+
piece.pass_on(msg, take_function(how_many -1))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def expand_function(expansion)
|
63
|
+
->(piece, msg) do
|
64
|
+
next_piece = Inlet.new(piece.destination, :not_done).flow(expansion.call(msg))
|
65
|
+
Piece.new(next_piece, expand_function(expansion))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def map_function(transform)
|
70
|
+
->(piece, msg) do
|
71
|
+
piece.pass_on(transform.call(msg), map_function(transform))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def filter_function(predicate)
|
76
|
+
->(piece, msg) do
|
77
|
+
if(predicate.call(msg)) then
|
78
|
+
piece.pass_on(msg, filter_function(predicate))
|
79
|
+
else
|
80
|
+
piece #don't change
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Aqueductron
|
2
|
+
class CompoundResult
|
3
|
+
include Result
|
4
|
+
def initialize(paths)
|
5
|
+
@contents = paths
|
6
|
+
end
|
7
|
+
|
8
|
+
def keys
|
9
|
+
@contents.keys
|
10
|
+
end
|
11
|
+
|
12
|
+
def value(*path)
|
13
|
+
return self if path.empty?
|
14
|
+
(head, *tail) = path
|
15
|
+
puts "Nothing found at #{head}" unless @contents[head]
|
16
|
+
@contents[head].value(*tail)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'piece_common'
|
2
|
+
|
3
|
+
module Aqueductron
|
4
|
+
class CountingEndPiece
|
5
|
+
include PieceCommon
|
6
|
+
def initialize(so_far = 0)
|
7
|
+
@so_far = so_far
|
8
|
+
end
|
9
|
+
def eof
|
10
|
+
SimpleResult.new(@so_far)
|
11
|
+
end
|
12
|
+
def receive msg
|
13
|
+
CountingEndPiece.new(@so_far + 1)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'buildering'
|
2
|
+
|
3
|
+
module Aqueductron
|
4
|
+
class Duct
|
5
|
+
include Buildering
|
6
|
+
def initialize(things_so_far = [])
|
7
|
+
@do_these_things = things_so_far
|
8
|
+
end
|
9
|
+
|
10
|
+
def attach(piece)
|
11
|
+
Duct.new(@do_these_things + [piece])
|
12
|
+
end
|
13
|
+
|
14
|
+
def answer_int(piece)
|
15
|
+
if (@do_these_things.empty?)
|
16
|
+
piece
|
17
|
+
else
|
18
|
+
answer_int(Piece.new(piece, @do_these_things.pop))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'piece_common'
|
2
|
+
require_relative 'simple_result'
|
3
|
+
|
4
|
+
module Aqueductron
|
5
|
+
class EndPiece
|
6
|
+
include PieceCommon
|
7
|
+
def initialize(monoid, so_far = :no_value)
|
8
|
+
@monoid = monoid
|
9
|
+
@so_far = (so_far == :no_value) ? monoid.zero : so_far
|
10
|
+
end
|
11
|
+
|
12
|
+
def eof
|
13
|
+
SimpleResult.new(@so_far)
|
14
|
+
end
|
15
|
+
|
16
|
+
def receive msg
|
17
|
+
EndPiece.new(@monoid, @monoid.append(@so_far, msg))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Aqueductron
|
2
|
+
class Inlet
|
3
|
+
def initialize(next_piece, done_or_not = :done)
|
4
|
+
@next_piece = next_piece
|
5
|
+
@done_or_not = done_or_not
|
6
|
+
end
|
7
|
+
|
8
|
+
def flow(source)
|
9
|
+
flow_internal(source.each)
|
10
|
+
end
|
11
|
+
|
12
|
+
def flow_internal(source)
|
13
|
+
result = begin
|
14
|
+
response = @next_piece.receive(source.next)
|
15
|
+
if (response.result?) then
|
16
|
+
response
|
17
|
+
else #it's another piece
|
18
|
+
Inlet.new(response, @done_or_not).flow_internal(source)
|
19
|
+
end
|
20
|
+
rescue StopIteration
|
21
|
+
if (@done_or_not == :done) then
|
22
|
+
@next_piece.eof
|
23
|
+
else
|
24
|
+
@next_piece
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'compound_result'
|
2
|
+
require_relative 'piece_common'
|
3
|
+
|
4
|
+
module Aqueductron
|
5
|
+
class JointPiece
|
6
|
+
def initialize(paths)
|
7
|
+
@paths = paths
|
8
|
+
end
|
9
|
+
include PieceCommon
|
10
|
+
|
11
|
+
def receive(msg)
|
12
|
+
go = ->(v) { v.result? ? v : v.receive(msg) }
|
13
|
+
new_map = @paths.map_values(&go)
|
14
|
+
if (new_map.values.all? &:result? )
|
15
|
+
construct_compound_result(new_map)
|
16
|
+
else
|
17
|
+
JointPiece.new(new_map)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def eof
|
22
|
+
go = ->(v) { v.result? ? v : v.eof }
|
23
|
+
new_map = @paths.map_values(&go)
|
24
|
+
construct_compound_result(new_map)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def construct_compound_result(paths)
|
29
|
+
CompoundResult.new(paths)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'piece_common'
|
2
|
+
|
3
|
+
module Aqueductron
|
4
|
+
class LastEndPiece
|
5
|
+
include PieceCommon
|
6
|
+
def initialize(most_recent = :no_data)
|
7
|
+
@most_recent = most_recent
|
8
|
+
end
|
9
|
+
def eof
|
10
|
+
SimpleResult.new(@most_recent)
|
11
|
+
end
|
12
|
+
def receive msg
|
13
|
+
LastEndPiece.new(msg)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Aqueductron
|
2
|
+
class Monoid
|
3
|
+
attr_reader :zero
|
4
|
+
def initialize(zero, add_lambda)
|
5
|
+
@zero = zero
|
6
|
+
@append = add_lambda
|
7
|
+
end
|
8
|
+
def append(a,b)
|
9
|
+
@append.call(a,b)
|
10
|
+
end
|
11
|
+
|
12
|
+
# instances
|
13
|
+
def self.concat
|
14
|
+
Monoid.new("", ->(a,b) {a + b})
|
15
|
+
end
|
16
|
+
def self.plus
|
17
|
+
Monoid.new(0, ->(a,b) {a + b})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
require_relative 'compound_result'
|
3
|
+
require_relative 'piece_common'
|
4
|
+
|
5
|
+
module Aqueductron
|
6
|
+
class SpontaneousJointPiece
|
7
|
+
attr_reader(:paths, :categorize, :make_new_path) # TODO: make private
|
8
|
+
def initialize(paths, categorize, make_new_path)
|
9
|
+
@categorize = categorize
|
10
|
+
@make_new_path = make_new_path
|
11
|
+
@paths = paths
|
12
|
+
end
|
13
|
+
include PieceCommon
|
14
|
+
|
15
|
+
def receive(msg)
|
16
|
+
category = categorize.call(msg)
|
17
|
+
new_map = if (paths.key? category)
|
18
|
+
paths
|
19
|
+
else
|
20
|
+
paths[category] = make_new_path.call(category) # todo: don't update
|
21
|
+
paths
|
22
|
+
end
|
23
|
+
go = ->(v) { v.result? ? v : v.receive(msg) }
|
24
|
+
new_map[category] = go.call(new_map[category]) #todo: don't mutate
|
25
|
+
if (new_map.values.all? &:result? )
|
26
|
+
construct_compound_result(new_map)
|
27
|
+
else
|
28
|
+
SpontaneousJointPiece.new(new_map, categorize, make_new_path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def eof
|
33
|
+
go = ->(v) { v.result? ? v : v.eof }
|
34
|
+
new_map = paths.map_values(&go)
|
35
|
+
construct_compound_result(new_map)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def construct_compound_result(paths)
|
40
|
+
CompoundResult.new(paths)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative 'piece_common'
|
2
|
+
|
3
|
+
module Aqueductron
|
4
|
+
class Piece
|
5
|
+
attr_reader :destination
|
6
|
+
include PieceCommon
|
7
|
+
|
8
|
+
def initialize(destination, what_to_do)
|
9
|
+
@destination = destination
|
10
|
+
@what_to_do = what_to_do
|
11
|
+
end
|
12
|
+
|
13
|
+
def receive(msg)
|
14
|
+
@what_to_do.call(self, msg)
|
15
|
+
end
|
16
|
+
|
17
|
+
def eof
|
18
|
+
send_eof
|
19
|
+
end
|
20
|
+
|
21
|
+
def pass_on(msg, what_to_do_next)
|
22
|
+
next_destination = @destination.receive(msg)
|
23
|
+
if (next_destination.result?) then
|
24
|
+
next_destination
|
25
|
+
else
|
26
|
+
Piece.new(next_destination, what_to_do_next)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
def send_eof
|
30
|
+
@destination.eof
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aqueductron
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jessica Kerr
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-03-04 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Aqueductron lets you create a path for data, then flow data through it,
|
14
|
+
then find the results at the end. It encourages a functional style, avoiding mutable
|
15
|
+
values. It uses iteratees to let the path change itself as the data passes.
|
16
|
+
email: jessitron@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/aqueductron/buildering.rb
|
22
|
+
- lib/aqueductron/compound_result.rb
|
23
|
+
- lib/aqueductron/counting_end_piece.rb
|
24
|
+
- lib/aqueductron/duct.rb
|
25
|
+
- lib/aqueductron/end_piece.rb
|
26
|
+
- lib/aqueductron/inlet.rb
|
27
|
+
- lib/aqueductron/joint_piece.rb
|
28
|
+
- lib/aqueductron/last_end_piece.rb
|
29
|
+
- lib/aqueductron/monoid.rb
|
30
|
+
- lib/aqueductron/partition.rb
|
31
|
+
- lib/aqueductron/piece.rb
|
32
|
+
- lib/aqueductron/piece_common.rb
|
33
|
+
- lib/aqueductron/result.rb
|
34
|
+
- lib/aqueductron/simple_result.rb
|
35
|
+
- lib/aqueductron.rb
|
36
|
+
- lib/improved_hash.rb
|
37
|
+
homepage: http://github.com/jessitron/aqueductron
|
38
|
+
licenses:
|
39
|
+
- MIT
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 2.0.3
|
58
|
+
signing_key:
|
59
|
+
specification_version: 4
|
60
|
+
summary: Dataflow in a functional, self-modifying pipeline style
|
61
|
+
test_files: []
|