aqueductron 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/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: []
|