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