aqueductron 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ require_relative 'aqueductron/duct'
2
+ require_relative 'aqueductron/monoid'
3
+ require_relative 'improved_hash'
@@ -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
@@ -0,0 +1,10 @@
1
+ module Aqueductron
2
+ module PieceCommon
3
+ def flow(source)
4
+ Inlet.new(self).flow_internal(source.each)
5
+ end
6
+ def result?
7
+ false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module Aqueductron
2
+ module Result
3
+ def result?
4
+ true
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'result'
2
+
3
+ module Aqueductron
4
+ class SimpleResult
5
+ include Result
6
+ def initialize(value)
7
+ @value = value
8
+ end
9
+
10
+ def keys
11
+ []
12
+ end
13
+
14
+ def value
15
+ @value
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,6 @@
1
+
2
+ class Hash
3
+ def map_values &block
4
+ self.each_with_object({}) {|(k,v), h| h[k] = block.call(v) }
5
+ end
6
+ 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: []