pipetree 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa9de99071c198cb1ac6254f7c5fc9eddbc3eaa1
4
- data.tar.gz: ceb3b6450907aeccd607e8704fc78a24fbcd2b87
3
+ metadata.gz: 9d2f95504872630ac29dd00818a64ab869910211
4
+ data.tar.gz: 1df1dc4a468ad5e4ab2970f70e6bcae19c0e3a22
5
5
  SHA512:
6
- metadata.gz: d57ed120a07f1ab17b84c96539eefad984a702156f72dfd4d3e86aa03e6185df9b1786ffe7af666d75a4dfd1168ffd4b090ffd89b98a8d1f757dbc2fc1c1012a
7
- data.tar.gz: 75183aa0a9538807dbd9e3d7fc832d7645618d6275be743ff9234aa4fbd4512d937917e24a73ea6351734572758adf3a71668a43af0a2186460e0b20afa9e067
6
+ metadata.gz: b0f00369c97fbe68c5b96910166d34cf06b172d23f2ce30a97a19962faa2c5f5dbec91fd07750e9840451d5bc24c18ecff6ed48ac41f4bf7195e0ad079dc2c88
7
+ data.tar.gz: 43e323582b82f168b93835d0fe4ae4c9b1b5d1989db3e70ee2d4a0f2d0d72f8b2580c74778dd6cb72ba9f6d69b0b121b27168aa385bbba4778c8d3ab148eb130
data/CHANGES.md CHANGED
@@ -1,3 +1,13 @@
1
+ # 0.1.0
2
+
3
+ * Rename `Flow` to `Railway`.
4
+ * All "operators" like `Flow#&` are now optional in `Operator` and considered experimental. Single entry point to adding steps is `Flow#add`.
5
+ * Remove `Flow#>>`.
6
+ * Add `Flow#add` which allows a low-level *tie*, a step that sits directly on the track with incoming interface `(last, input, options)` and outgoing `[new_track, input]`.
7
+ * Make `Flow#_insert` private, as we now have `add`.
8
+ * `And`'s direction can now be configured via `:on_true` and `:on_false`, allowing to deviate to any kind of track.
9
+ * Remove the `Pipetree` array implementation and save it for future versions.
10
+
1
11
  # 0.0.5
2
12
 
3
13
  * `Flow` doesn't inherit from `Array` anymore. Temporarily use `Uber::Delegates` to achieve the same interface. This will change in 0.1.x when `Insert` works against an array. This makes `Flow.new` the new canonical constructor which allows us to initialize `step@proc` properly.
data/README.md CHANGED
@@ -16,6 +16,12 @@ Instead of implementing the perfect API where users can override methods, call `
16
16
 
17
17
  This is way less tedious than climbing through `super` calls and callstacks.
18
18
 
19
+ ##
20
+
21
+ [ tie, tie, tie ]
22
+ On.new(track, strut)
23
+ Right.new( step )
24
+
19
25
  ## Installation
20
26
 
21
27
  In your `Gemfile`.
@@ -1,13 +1,14 @@
1
1
  module Pipetree::Function
2
2
  class Insert
3
- def call(arr, func, options)
3
+ Operations = [:delete, :replace, :before, :after, :append, :prepend]
4
+
5
+ # DISCUSS: all methods write to original array.
6
+ def call(arr, operation, *args)
4
7
  # arr = arr.dup
5
- operations = [:delete, :replace, :before, :after, :append, :prepend]
8
+ raise "[Pipetree] Unknown Insert operation #{args.inspect}" unless Operations.include?(operation)
6
9
 
7
- # replace!(arr, options[:replace], func)
8
- options.keys.reverse.each { |k| operations.include?(k) and return send("#{k}!", arr, options[k], func) }
10
+ send("#{operation}!", arr, *args) # replace!(arr, Old, New)
9
11
 
10
- raise "[Pipetree] Unknown command #{options.inspect}" # TODO: test.
11
12
  # arr
12
13
  end
13
14
 
@@ -25,17 +26,10 @@ module Pipetree::Function
25
26
  arr
26
27
  end
27
28
 
28
- def delete!(arr, _, removed_func)
29
- index = arr.index(removed_func)
29
+ def delete!(arr, old_func, *)
30
+ index = arr.index(old_func)
30
31
  arr.delete_at(index)
31
32
 
32
- # TODO: make nice.
33
- # arr.each_with_index { |func, index|
34
- # if func.is_a?(Collect)
35
- # arr[index] = Collect[*Pipeline::Insert.(func, removed_func, delete: true)]
36
- # end
37
- # }
38
-
39
33
  arr
40
34
  end
41
35
 
@@ -57,15 +51,7 @@ module Pipetree::Function
57
51
  def prepend!(arr, old_func, new_func)
58
52
  arr.unshift(new_func)
59
53
  end
60
-
61
- module Macros
62
- def insert!(new_function, options)
63
- Pipetree::Insert.(self, new_function, options)
64
- end
65
- end
66
54
  end
67
55
  end
68
56
 
69
57
  Pipetree::Insert = Pipetree::Function::Insert.new
70
-
71
- #FIXME: all methods write to original array.
@@ -10,18 +10,8 @@ module Pipetree::Inspect
10
10
  inspect_rows(names)
11
11
  end
12
12
 
13
- # open source file to retrieve the constant name.
14
13
  def inspect_func(func)
15
- return inspect_object(func) unless func.is_a?(Proc)
16
- inspect_proc(func)
17
- end
18
-
19
- def inspect_object(obj)
20
- obj.inspect.sub(/0x\w+/, "")
21
- end
22
-
23
- def inspect_proc(proc)
24
- File.readlines(proc.source_location[0])[proc.source_location[1]-1].match(/^\s+([\w\:]+)/)[1]
14
+ func
25
15
  end
26
16
 
27
17
  def inspect_line(names)
@@ -1,26 +1,22 @@
1
1
  require "pipetree/inspect"
2
2
 
3
- module Pipetree::Flow::Inspect
3
+ module Pipetree::Railway::Inspect
4
4
  include ::Pipetree::Inspect
5
5
 
6
- Proc = Struct.new(:name, :proc, :operator)
7
-
8
6
  def inspect_func(step)
9
- cfg = @step2proc[step]
10
- [cfg.name || super(cfg.proc), cfg.operator]
7
+ @inspect[step]
11
8
  end
12
9
 
10
+ Operator = { Pipetree::Railway::Left => "<", Pipetree::Railway::Right => ">", }
11
+
13
12
  def inspect_line(names)
14
- string = names.collect { |i, name| "#{name.last}#{name.first}" }.join(",")
13
+ string = names.collect { |i, (track, name)| "#{Operator[track]}#{name}" }.join(",")
15
14
  "[#{string}]"
16
15
  end
17
16
 
18
- def inspect_row(index, name)
19
- "#{index} #{name.last}#{name.first}"
20
- end
21
-
22
17
  def inspect_rows(names)
23
- string = names.collect do |i, (name, operator)|
18
+ string = names.collect do |i, (track, name)|
19
+ operator = Operator[track]
24
20
 
25
21
  op = "#{operator}#{name}"
26
22
  padding = 38
@@ -0,0 +1,29 @@
1
+ # Optimize the most common steps with Stay/And objects that are faster than procs.
2
+ # This is experimental API and might be removed/changed without prior warning.
3
+ class Pipetree::Railway
4
+ module Operator
5
+ def <(proc, options={})
6
+ _insert(Pipetree::Railway.<(proc), options, Left, proc)
7
+ end
8
+
9
+ def &(proc, options={})
10
+ _insert(Pipetree::Railway.&(proc), options, Right, proc)
11
+ end
12
+
13
+ def >(proc, options={})
14
+ _insert(Pipetree::Railway.>(proc), options, Right, proc)
15
+ end
16
+ end
17
+
18
+ def self.<(proc)
19
+ On.new(Left, Stay.new(proc))
20
+ end
21
+
22
+ def self.&(proc)
23
+ On.new(Right, And.new(proc))
24
+ end
25
+
26
+ def self.>(proc)
27
+ On.new(Right, Stay.new(proc))
28
+ end
29
+ end
@@ -0,0 +1,103 @@
1
+ class Pipetree
2
+ class Railway
3
+ require "pipetree/insert"
4
+ require "pipetree/railway/operator"
5
+
6
+ def initialize(*args)
7
+ @steps = Array.new(*args)
8
+ @index = Hash.new
9
+ @inspect = Hash.new
10
+ end
11
+
12
+ # Actual implementation of Pipetree:Railway. Yes, it's that simple!
13
+ def call(input, options)
14
+ input = [Right, input]
15
+
16
+ @steps.inject(input) do |(last, memo), step|
17
+ step.call(last, memo, options)
18
+ end
19
+ end
20
+
21
+ # Naming:
22
+ # * Track
23
+ # * Tie: the callable that's usually an On instance and is sitting directly in the pipe, on a Track.
24
+ # * Strut: the callable that's wrapped by the Tie and implements the decider logic (e.g. And).
25
+ # * Step: the user callable with interface `Step.(input, options)`.
26
+ module Add
27
+ def add(track, strut, options={})
28
+ _insert On.new(track, strut), options, track, strut
29
+ end
30
+
31
+ require "uber/delegates"
32
+ extend Uber::Delegates
33
+ # TODO: make Insert properly decoupled! it still relies on Array interface on pipe`.
34
+ delegates :@steps, :<<, :each_with_index, :[]=, :delete_at, :insert, :unshift, :index
35
+
36
+ private
37
+ def _insert(tie, options, track, strut)
38
+ insert_operation = (options.keys & ::Pipetree::Function::Insert::Operations).last || :append
39
+
40
+ old_tie = @index[ options[insert_operation] ] # name --> tie
41
+
42
+ # todo: step, old_tie (e.g. for #delete!).
43
+ Insert.(self, insert_operation, old_tie, tie)
44
+
45
+ @index[options[:name]] = tie
46
+ @inspect[tie] = [ track, options[:name] ]
47
+
48
+ self
49
+ end
50
+ end
51
+ include Add
52
+
53
+ # Tracks emitted by steps.
54
+ Track = Class.new
55
+ Left = Class.new(Track)
56
+ Right = Class.new(Track)
57
+
58
+ # Incoming direction must be Left/Right.
59
+ # Tie
60
+ class On
61
+ def initialize(track, proc)
62
+ @track, @proc = track, proc
63
+ end
64
+
65
+ def call(last, input, options)
66
+ return [last, input] unless last == @track # return unless incoming track is Right (or Left).
67
+ @proc.(last, input, options)
68
+ end
69
+ end
70
+
71
+ class Strut
72
+ def initialize(proc, config={})
73
+ @proc = proc
74
+ @config = config
75
+ end
76
+
77
+ def call(last, input, options)
78
+ result = @proc.(input, options) # call the actual step.
79
+
80
+ [self.class::Decider.(result, @config, last, input, options), input] # decide about the track and return Flow-compliant response.
81
+ end
82
+ end
83
+
84
+ # Call step proc and return (Right || Left).
85
+ class And < Strut
86
+ # Deciders return the new track.
87
+ Decider = ->(result, config, *) do
88
+ result ?
89
+ config[:on_true] || Right :
90
+ config[:on_false] || Left
91
+ end
92
+ end
93
+
94
+ # Call step proc and return incoming last step.
95
+ class Stay < Strut
96
+ # simply pass through the current direction: e.g. Left or Right.
97
+ Decider = ->(result, cfg, last, *) { last }
98
+ end
99
+
100
+ require "pipetree/railway/inspect"
101
+ include Inspect
102
+ end
103
+ end
@@ -1,3 +1,3 @@
1
1
  class Pipetree < Array
2
- VERSION = "0.0.5"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/pipetree.rb CHANGED
@@ -1,55 +1,2 @@
1
- class Pipetree < Array
2
- # Allows to implement a pipeline of filters where a value gets passed in and the result gets
3
- # passed to the next callable object.
4
- Stop = Class.new
5
-
6
- # options is mutuable.
7
- # we have a fixed set of arguments here, since array splat significantly slows this down, as in
8
- # call(input, *options)
9
- def call(input, options)
10
- inject(input) do |memo, step|
11
- res = evaluate(step, memo, options)
12
- return(Stop) if Stop == res
13
- res
14
- end
15
- end
16
-
17
- private
18
- def evaluate(step, input, options)
19
- step.call(input, options)
20
- end
21
-
22
- require "pipetree/inspect"
23
- include Inspect
24
-
25
- module Macros
26
- def insert!(new_function, options)
27
- Pipetree::Insert.(self, new_function, options)
28
- end
29
- end
30
- require "pipetree/insert"
31
- include Macros
32
-
33
- # Collect applies a pipeline to each element of input.
34
- class Collect < self
35
- # when stop, the element is skipped. (should that be Skip then?)
36
- def call(input, options)
37
- arr = []
38
- input.each_with_index do |item_fragment, i|
39
- result = super(item_fragment, options.merge(index: i)) # DISCUSS: NO :fragment set.
40
- Stop == result ? next : arr << result
41
- end
42
- arr
43
- end
44
-
45
- # DISCUSS: will this make it into the final version?
46
- class Hash < self
47
- def call(input, options)
48
- {}.tap do |hsh|
49
- input.each { |key, item_fragment|
50
- hsh[key] = super(item_fragment, options) }# DISCUSS: NO :fragment set.
51
- end
52
- end
53
- end
54
- end
1
+ class Pipetree
55
2
  end
@@ -1,48 +1,21 @@
1
1
  require "test_helper"
2
2
 
3
- class AlteringTest < Minitest::Spec
4
- A = ->(*) { "bla ruby 1.9 needs that" }
5
- B = ->(*) { }
6
- C = ->(*) { "otherwise it'll confuse empty procs" }
7
-
8
- # constructor.
9
- it do
10
- pipe = ::Pipetree[A, B]
11
- pipe.inspect.must_equal %{[A|>B]}
12
- end
13
-
14
- it { Pipetree[].insert(0, B).inspect.must_equal %{[B]} }
15
- it { Pipetree[].unshift(B).inspect.must_equal %{[B]} }
16
- it { Pipetree[].unshift(B, A).inspect.must_equal %{[B|>A]} }
17
-
18
- it { Pipetree[A,B].insert!(C, before: A).inspect.must_equal %{[C|>A|>B]} }
19
- it { Pipetree[A,B].insert!(C, before: B).inspect.must_equal %{[A|>C|>B]} }
20
-
21
- it { Pipetree[A,B].insert!(C, after: A).inspect.must_equal %{[A|>C|>B]} }
22
- it { Pipetree[A,B].insert!(C, after: B).inspect.must_equal %{[A|>B|>C]} }
23
-
24
- it { Pipetree[A,B].insert!(C, append: true).inspect.must_equal %{[A|>B|>C]} }
25
- it { Pipetree[].insert!(C, append: true).inspect.must_equal %{[C]} }
26
-
27
- it { Pipetree[A,B].insert!(C, prepend: true).inspect.must_equal %{[C|>A|>B]} }
28
- it { Pipetree[].insert!(C, prepend: true).inspect.must_equal %{[C]} }
29
-
30
- # last option wins
31
- it { Pipetree[A,B].insert!(C, after: B, prepend: true).inspect.must_equal %{[C|>A|>B]} }
32
- end
33
-
34
- require "pipetree/flow"
35
- class FlowInsertTest < Minitest::Spec
3
+ require "pipetree/railway"
4
+ class RailwayInsertTest < Minitest::Spec
36
5
  A = ->{ }
37
6
  B = ->{ }
38
7
  C = ->{ }
39
8
 
40
- it { pipe = Pipetree::Flow.new.>(A).>(B).inspect.must_equal %{[>A,>B]} }
41
- it { pipe = Pipetree::Flow.new.>(A).>(B, before: A).inspect.must_equal %{[>B,>A]} }
42
- it { pipe = Pipetree::Flow.new.>(A).>(B).>(C, after: A).inspect.must_equal %{[>A,>C,>B]} }
43
- it { pipe = Pipetree::Flow.new.>(A).>(C, append: true).inspect.must_equal %{[>A,>C]} }
44
- it { pipe = Pipetree::Flow.new.>(A).>(C, prepend: true).inspect.must_equal %{[>C,>A]} }
45
- it { pipe = Pipetree::Flow.new.>(A).>(C, replace: A).inspect.must_equal %{[>C]} }
46
- it { pipe = Pipetree::Flow.new.>(A)._insert(A, {delete: true}, nil, nil).inspect.must_equal %{[]} }
47
- # FIXME: add :delete and :replace.
9
+ let (:pipe) { Pipetree::Railway.new.extend(Pipetree::Railway::Operator) }
10
+
11
+ it { pipe.>(A, name: :A).>(B, name: :B).inspect.must_equal %{[>A,>B]} }
12
+ it { pipe.>(A, name: :A).>(B, before: :A, name: :B).inspect.must_equal %{[>B,>A]} }
13
+ it { pipe.>(A, name: :A).>(B, name: :B).>(C, after: :A, name: :C).inspect.must_equal %{[>A,>C,>B]} }
14
+ it { pipe.>(A, name: "A").>(C, append: true, name: :C).inspect.must_equal %{[>A,>C]} }
15
+ it { pipe.>(A, name: :A).>(C, prepend: true, name: "C").inspect.must_equal %{[>C,>A]} }
16
+ it { pipe.>(A, name: :A).>(C, replace: :A, name: :C).inspect.must_equal %{[>C]} }
17
+ it { pipe.>(A, name: :A).add(nil, :A, delete: :A).inspect.must_equal %{[]} }
18
+ # last insert operation wins.
19
+ it { pipe.>(A, name: :A).>(B, name: :B).>(C, name: :C, after: :A, before: :A).inspect.must_equal %{[>C,>A,>B]} }
20
+ it { pipe.>(A, name: :A).>(B, name: :B).>(C, name: :C, before: :A, after: :A).inspect.must_equal %{[>A,>C,>B]} }
48
21
  end
data/test/inspect_test.rb CHANGED
@@ -1,35 +1,35 @@
1
- require "test_helper"
1
+ # require "test_helper"
2
2
 
3
- class InspectTest < Minitest::Spec
4
- module M
5
- end
3
+ # class InspectTest < Minitest::Spec
4
+ # module M
5
+ # end
6
6
 
7
- M::AlphaConstant = ->(*) { }
8
- M::Beta = ->(*) { }
9
- Callable = Object.new
7
+ # M::AlphaConstant = ->(*) { }
8
+ # M::Beta = ->(*) { }
9
+ # Callable = Object.new
10
10
 
11
- let (:pipe) { ::Pipetree[M::Beta, M::AlphaConstant, M::Beta, M::AlphaConstant, M::Beta, M::AlphaConstant, M::Beta, M::AlphaConstant, M::Beta, M::AlphaConstant, M::Beta, Callable] }
11
+ # let (:pipe) { ::Pipetree[M::Beta, M::AlphaConstant, M::Beta, M::AlphaConstant, M::Beta, M::AlphaConstant, M::Beta, M::AlphaConstant, M::Beta, M::AlphaConstant, M::Beta, Callable] }
12
12
 
13
- it do
14
- puts pipe.inspect
15
- puts pipe.inspect(style: :rows)
13
+ # it do
14
+ # puts pipe.inspect
15
+ # puts pipe.inspect(style: :rows)
16
16
 
17
17
 
18
- pipe.inspect(style: :rows).must_equal %{
19
- 0|>M::Beta
20
- 1|>M::AlphaConstant
21
- 2|>M::Beta
22
- 3|>M::AlphaConstant
23
- 4|>M::Beta
24
- 5|>M::AlphaConstant
25
- 6|>M::Beta
26
- 7|>M::AlphaConstant
27
- 8|>M::Beta
28
- 9|>M::AlphaConstant
29
- 10|>M::Beta
30
- 11|>#<Object:>}
31
- end
18
+ # pipe.inspect(style: :rows).must_equal %{
19
+ # 0|>M::Beta
20
+ # 1|>M::AlphaConstant
21
+ # 2|>M::Beta
22
+ # 3|>M::AlphaConstant
23
+ # 4|>M::Beta
24
+ # 5|>M::AlphaConstant
25
+ # 6|>M::Beta
26
+ # 7|>M::AlphaConstant
27
+ # 8|>M::Beta
28
+ # 9|>M::AlphaConstant
29
+ # 10|>M::Beta
30
+ # 11|>#<Object:>}
31
+ # end
32
32
 
33
- # different separator
34
- it { ::Pipetree[M::AlphaConstant,M::Beta,Callable].inspect.must_equal %{[M::AlphaConstant|>M::Beta|>#<Object:>]} }
35
- end
33
+ # # different separator
34
+ # it { ::Pipetree[M::AlphaConstant,M::Beta,Callable].inspect.must_equal %{[M::AlphaConstant|>M::Beta|>#<Object:>]} }
35
+ # end
@@ -0,0 +1,183 @@
1
+ require "test_helper"
2
+ require "pipetree/railway"
3
+ require "json"
4
+
5
+ class RailwayTest < Minitest::Spec
6
+ F = Pipetree::Railway
7
+
8
+ describe "#add" do
9
+ let (:pipe) do
10
+ pipe = F.new
11
+
12
+ # add: (track, proc)
13
+ # macro returns [signal, input]: (can be added to pipe via #add)
14
+ step_1 = F::And.new(->(input, options) { options["x"] = true })
15
+ step_2 = F::And.new(->(input, options) { input })
16
+ step_3 = F::And.new(->(input, options) { options["step_3"] = true })
17
+
18
+ fail_1 = F::Stay.new(->(input, options) { options["fail_1"] = true })
19
+ fail_2 = F::And.new(->(input, options) { options["fail_2"] = true }, on_true: left_1, on_false: F::Left)
20
+ fail_3 = F::Stay.new(->(input, options) { options["fail_3"] = true })
21
+
22
+
23
+ pipe.add(F::Right, step_1)
24
+ pipe.add(F::Right, step_2)
25
+ pipe.add(F::Left, fail_1)
26
+ pipe.add(F::Left, fail_2)
27
+ pipe.add(F::Left, fail_3)
28
+ pipe.add(F::Right, step_3)
29
+ end
30
+
31
+ let (:left_1) { Class.new(F::Left) }
32
+
33
+ # only right
34
+ it { [pipe.(true, options={}), options].must_equal [[F::Right, true], {"x"=>true, "step_3"=>true}] }
35
+ # jumps to left at step_2
36
+ it { [pipe.(false, options={}), options].must_equal [[left_1, false], {"x"=>true, "fail_1"=>true, "fail_2"=>true}] }
37
+
38
+ # options for #add
39
+ # chainability of #add.
40
+ it do
41
+ F.new
42
+ .add(F::Right, Object, name: "operation.new")
43
+ .add(F::Right, Module, name: "nested.create", before: "operation.new")
44
+ .inspect.must_equal %{[>nested.create,>operation.new]}
45
+ end
46
+ end
47
+
48
+ Aaa = ->(*) { "yo" }
49
+ B = ->(*) { }
50
+
51
+ let (:pipe) { pipe = Pipetree::Railway.new.extend(Pipetree::Railway::Operator)
52
+ pipe.& ->(value, options) { value && options["deserializer.result"] = JSON.parse(value) }
53
+ pipe.& ->(value, options) { options["deserializer.result"]["key"] == 1 ? true : (options["contract.errors"]=false) }
54
+ pipe.& ->(value, options) { options["deserializer.result"]["key2"] == 2 ? true : (options["contract.errors.2"]="screwd";false) }
55
+ pipe.< ->(value, options) { options["after_deserialize.fail"]=true }
56
+ pipe.> ->(value, options) { options["meantime"] = true }
57
+ pipe.< ->(value, options) { options["after_meantime.left?"]=true; false } # false is ignored.
58
+
59
+ }
60
+
61
+ # success?
62
+ it do
63
+ options = {}
64
+ pipe.(%{{"key": 1,"key2":2}}, options)#.must_equal ""
65
+
66
+ options.must_equal({"deserializer.result"=>{"key"=>1, "key2"=>2}, "meantime"=>true})
67
+ end
68
+
69
+ # invalid?
70
+ it do
71
+ options = {}
72
+ pipe.(%{{"key": 2}}, options)#.must_equal ""
73
+
74
+ options.must_equal({"deserializer.result"=>{"key"=>2}, "contract.errors"=>false, "after_deserialize.fail"=>true, "after_meantime.left?"=>true})
75
+ end
76
+
77
+ it do
78
+ options = {}
79
+ pipe.(%{{"key": 1,"key2":null}}, options)#.must_equal ""
80
+
81
+ options.must_equal({"deserializer.result"=>{"key"=>1, "key2"=>nil}, "contract.errors.2"=>"screwd", "after_deserialize.fail"=>true, "after_meantime.left?"=>true})
82
+ end
83
+
84
+ #---
85
+ # return value is new input.
86
+ it do
87
+ pipe = Pipetree::Railway.new [
88
+ Pipetree::Railway::On.new(Pipetree::Railway::Right, ->(last, value, options) { [Pipetree::Railway::Right, value.reverse] } )
89
+ ]
90
+ pipe.("Hello", {}).must_equal [Pipetree::Railway::Right, "olleH"]
91
+ end
92
+
93
+ #---
94
+ # #>
95
+ describe "#>" do
96
+ let (:pipe) { Pipetree::Railway.new.extend(Pipetree::Railway::Operator) }
97
+ it {
98
+ pipe.> ->(input, options) { input.reverse }
99
+ # pipe.| B
100
+ # pipe.% A
101
+ pipe.("Hallo", {}).must_equal [Pipetree::Railway::Right, "Hallo"]
102
+ }
103
+ end
104
+
105
+ #---
106
+ # #inspect
107
+ Seventeen = ->(*) { snippet }
108
+ Long = ->(*) { snippet }
109
+ Callable = Object.new # random callable object.
110
+
111
+ describe "#inspect" do
112
+ let (:pipe) { Pipetree::Railway.new.extend(Pipetree::Railway::Operator).&(Aaa, name: "Aaa").<(B, name: "B").<(Seventeen, name: "Seventeen").>(Long, name: "Long").>(Callable, name: "Callable") }
113
+
114
+ it { pipe.inspect.must_equal %{[>Aaa,<B,<Seventeen,>Long,>Callable]} }
115
+
116
+ it { pipe.inspect(style: :rows).must_equal %{
117
+ 0 ==================================>Aaa
118
+ 1 <B====================================
119
+ 2 <Seventeen============================
120
+ 3 =================================>Long
121
+ 4 =============================>Callable} }
122
+ end
123
+
124
+ #---
125
+ # with aliases
126
+ it do
127
+ pipe = Pipetree::Railway.new.extend(Pipetree::Railway::Operator).
128
+ >(Aaa, name: "pipe.aaa").
129
+ >(B, name: "pipe.b").
130
+ >(Aaa, name: "pipe.aaa.aaa")
131
+
132
+ pipe.inspect.must_equal %{[>pipe.aaa,>pipe.b,>pipe.aaa.aaa]}
133
+ pipe.inspect(style: :rows).must_equal %{
134
+ 0 =============================>pipe.aaa
135
+ 1 ===============================>pipe.b
136
+ 2 =========================>pipe.aaa.aaa}
137
+
138
+ pipe.>(Long, after: "pipe.b", name: "Long").inspect.must_equal %{[>pipe.aaa,>pipe.b,>Long,>pipe.aaa.aaa]}
139
+ end
140
+
141
+ #---
142
+ # test decompose array
143
+ it do
144
+ pipe = Pipetree::Railway.new.extend(Pipetree::Railway::Operator).
145
+ &( ->((value, input), options) { input["x"] = value } ) # decomposes input.
146
+
147
+ options={key: 1}
148
+ input = {}
149
+
150
+ pipe.([options[:key], input], options).must_equal [Pipetree::Railway::Right, [1, {"x"=>1}]]
151
+ input.inspect.must_equal %{{"x"=>1}}
152
+ options.inspect.must_equal %{{:key=>1}}
153
+ end
154
+ end
155
+
156
+ class NestedPipeTest < Minitest::Spec
157
+ R = Pipetree::Railway
158
+
159
+ it do
160
+ nested_pipe = Pipetree::Railway.new.extend(Pipetree::Railway::Operator)
161
+ .&( ->(input, options) { options["extract"] = true } )
162
+ .&( ->(input, options) { options["validate"] = options[:success] } )
163
+
164
+ # This is basically what Nested() does.
165
+ pipe = R.new.add(R::Right,
166
+ R::On.new( R::Right, ->(last, input, options) {
167
+
168
+ signal, input = nested_pipe.(input, options)
169
+
170
+ [signal, input] } )
171
+ )
172
+
173
+
174
+ options = { success: true }
175
+ pipe.(Object, options).must_equal [Pipetree::Railway::Right, Object]
176
+
177
+ options.inspect.must_equal %{{:success=>true, \"extract\"=>true, \"validate\"=>true}}
178
+
179
+
180
+ pipe.(Object, options = { success: false }).must_equal [Pipetree::Railway::Left, Object]
181
+ options.inspect.must_equal %{{:success=>false, \"extract\"=>true, \"validate\"=>false}}
182
+ end
183
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pipetree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-12 00:00:00.000000000 Z
11
+ date: 2017-01-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: uber
@@ -79,19 +79,18 @@ files:
79
79
  - README.md
80
80
  - Rakefile
81
81
  - lib/pipetree.rb
82
- - lib/pipetree/flow.rb
83
- - lib/pipetree/flow/inspect.rb
84
- - lib/pipetree/flow/step_map.rb
85
82
  - lib/pipetree/insert.rb
86
83
  - lib/pipetree/inspect.rb
84
+ - lib/pipetree/railway.rb
85
+ - lib/pipetree/railway/inspect.rb
86
+ - lib/pipetree/railway/operator.rb
87
87
  - lib/pipetree/version.rb
88
88
  - pipetree.gemspec
89
89
  - test/altering_test.rb
90
90
  - test/benchmark_monads.rb
91
91
  - test/benchmarking.rb
92
- - test/flow_test.rb
93
92
  - test/inspect_test.rb
94
- - test/pipetree_test.rb
93
+ - test/railway_test.rb
95
94
  - test/test_helper.rb
96
95
  homepage: https://github.com/apotonick/pipetree
97
96
  licenses:
@@ -122,7 +121,6 @@ test_files:
122
121
  - test/altering_test.rb
123
122
  - test/benchmark_monads.rb
124
123
  - test/benchmarking.rb
125
- - test/flow_test.rb
126
124
  - test/inspect_test.rb
127
- - test/pipetree_test.rb
125
+ - test/railway_test.rb
128
126
  - test/test_helper.rb
@@ -1,24 +0,0 @@
1
- # Map original proc or its name to the wrapped On.
2
- # This class is solely dedicated for inspect and insert operations, and not
3
- # involved at run-time at all.
4
- class Pipetree::Flow::StepMap
5
- def initialize
6
- @hash = {}
7
- end
8
-
9
- def []=(step, (name, original_proc, operator))
10
- @hash[step] = Pipetree::Flow::Inspect::Proc.new(name, original_proc, operator)
11
- end
12
-
13
- def [](key)
14
- @hash[key]
15
- end
16
-
17
- def find_proc(original_proc)
18
- method = original_proc.is_a?(String) ? :name : :proc
19
-
20
- @hash.find do |step, inspect_proc|
21
- inspect_proc.send(method) == original_proc and return step
22
- end
23
- end
24
- end
data/lib/pipetree/flow.rb DELETED
@@ -1,111 +0,0 @@
1
- class Pipetree < Array
2
- class Flow
3
- require "pipetree/flow/inspect"
4
- include Inspect
5
- require "pipetree/flow/step_map"
6
- require "pipetree/insert"
7
-
8
- def initialize(*args)
9
- @steps = Array.new(*args)
10
- @step2proc = StepMap.new
11
- end
12
-
13
- # TODO: don't inherit from Array, because we don't want Array[].
14
-
15
- module Operators
16
- # Optimize the most common steps with Stay/And objects that are faster than procs.
17
- def <(proc, options={})
18
- _insert On.new(Left, Stay.new(proc)), options, proc, "<"
19
- end
20
-
21
- # OnRight-> ? Right, input : Left, input
22
- def &(proc, options={})
23
- _insert On.new(Right, And.new(proc)), options, proc, "&"
24
- end
25
-
26
- # TODO: test me.
27
- def >(proc, options={})
28
- _insert On.new(Right, Stay.new(proc)), options, proc, ">"
29
- end
30
-
31
- def >>(proc, options={})
32
- _insert On.new(Right,
33
- ->(last, input, options) { [Right, proc.(input, options)] } ), options, proc, ">>"
34
- end
35
-
36
- def %(proc, options={})
37
- # no condition is needed, and we want to stay on the same track, too.
38
- _insert Stay.new(proc), options, proc, "%"
39
- end
40
-
41
- # :private:
42
- # proc is the original step proc, e.g. Validate.
43
- def _insert(step, options, original_proc, operator)
44
- options = { append: true }.merge(options)
45
-
46
- insert!(step, options).tap do
47
- @step2proc[step] = options[:name], original_proc, operator
48
- end
49
-
50
- self
51
- end
52
-
53
- # :private:
54
- def index(proc) # @step2proc: { <On @proc> => {proc: @proc, name: "trb.validate", operator: "&"} }
55
- on = @step2proc.find_proc(proc) and return @steps.index(on)
56
- end
57
-
58
- require "uber/delegates"
59
- extend Uber::Delegates
60
- delegates :@steps, :<<, :each_with_index, :[]=, :delete_at, :insert, :unshift # FIXME: make Insert properly decoupled!
61
- end
62
- include Operators
63
-
64
- # Actual implementation of Pipetree:Flow. Yes, it's that simple!
65
- def call(input, options)
66
- input = [Right, input]
67
-
68
- @steps.inject(input) do |memooo, step|
69
- last, memo = memooo
70
- step.call(last, memo, options)
71
- end
72
- end
73
-
74
- # Directions emitted by steps.
75
- Left = Class.new
76
- Right = Class.new
77
-
78
- # Incoming direction must be Left/Right.
79
- class On
80
- def initialize(direction, proc)
81
- @direction, @proc = direction, proc
82
- end
83
-
84
- def call(last, input, options)
85
- return [last, input] unless last == @direction # return unless incoming direction is Right (or Left).
86
- @proc.(last, input, options)
87
- end
88
- end
89
-
90
- # Call step proc and return (Right || Left).
91
- class And
92
- def initialize(proc)
93
- @proc = proc
94
- end
95
-
96
- def call(last, input, options)
97
- @proc.(input, options) ? [Right, input] : [Left, input]
98
- end
99
- end
100
-
101
- # Call step proc and return incoming last step.
102
- class Stay < And
103
- def call(last, input, options)
104
- @proc.(input, options)
105
- [last, input] # simply pass through the current direction: either [Left, input] or [Right, input].
106
- end
107
- end
108
-
109
- include Function::Insert::Macros # #insert!
110
- end
111
- end
data/test/flow_test.rb DELETED
@@ -1,188 +0,0 @@
1
- require "test_helper"
2
-
3
- require "pipetree/flow"
4
-
5
- # pipe = Pipetree[
6
- # Pipetree::OnRight.new( ->(value, options) { #puts "|>DESERIALIZATION"
7
- # value.nil? ? [Pipetree::Left, value] : [Pipetree::Right, value] } ),
8
- # Pipetree::OnRight.new( ->(value, options) { #puts "|>VALIDATION"
9
- # value[:ok?] ? [Pipetree::Right, value] : [Pipetree::Left, value] } ),
10
- # Pipetree::OnRight.new( ->(value, options) { #puts "|>PERSISTENCE";
11
- # value[:persist?] ? [Pipetree::Right, value] : [Pipetree::Left, value] } ),
12
- # Pipetree::OnLeft.new( ->(value, options) { #puts "|| INVALID=CALLBACK";
13
- # [Pipetree::Left, value] } ),
14
- # Pipetree::OnRight.new( ->(value, options) { #puts "|>OK=CALLBACK";
15
- # [Pipetree::Right, value] } ),
16
- # Pipetree::OnLeft.new( ->(value, options) { #puts "|| SECOND-INVALID=CALLBACK";
17
- # [Pipetree::Left, value] } ),
18
-
19
- # Pipetree::OnLeft.new( ->(value, options) { #puts "|| FIXING IT=CALLBACK";
20
- # [Pipetree::Right, value] } ),
21
- # ]
22
-
23
- # puts "Pipetree"
24
- # pipe.({ok?: true}, {})
25
-
26
- require "json"
27
-
28
- class FlowTest < Minitest::Spec
29
- # TODO: test each function from & to > is only called once!
30
- # describe "#call" do
31
- # let (:pipe) { Pipetree::Flow.new }
32
- # it do
33
- # pipe.>> ->(*) { puts "snippet" }
34
- # pipe.({},{})
35
- # end
36
- # end
37
-
38
- Aaa = ->(*) { "yo" }
39
- B = ->(*) { }
40
-
41
- let (:pipe) { pipe = Pipetree::Flow.new
42
- pipe.& ->(value, options) { value && options["deserializer.result"] = JSON.parse(value) }
43
- pipe.& ->(value, options) { options["deserializer.result"]["key"] == 1 ? true : (options["contract.errors"]=false) }
44
- pipe.& ->(value, options) { options["deserializer.result"]["key2"] == 2 ? true : (options["contract.errors.2"]="screwd";false) }
45
- pipe.< ->(value, options) { options["after_deserialize.fail"]=true }
46
- pipe.% ->(value, options) { options["meantime"] = true }
47
- pipe.< ->(value, options) { options["after_meantime.left?"]=true; false } # false is ignored.
48
-
49
- }
50
-
51
- # success?
52
- it do
53
- options = {}
54
- pipe.(%{{"key": 1,"key2":2}}, options)#.must_equal ""
55
-
56
- options.must_equal({"deserializer.result"=>{"key"=>1, "key2"=>2}, "meantime"=>true})
57
- end
58
-
59
- # invalid?
60
- it do
61
- options = {}
62
- pipe.(%{{"key": 2}}, options)#.must_equal ""
63
-
64
- options.must_equal({"deserializer.result"=>{"key"=>2}, "contract.errors"=>false, "after_deserialize.fail"=>true, "meantime"=>true, "after_meantime.left?"=>true})
65
- end
66
-
67
- it do
68
- options = {}
69
- pipe.(%{{"key": 1,"key2":null}}, options)#.must_equal ""
70
-
71
- options.must_equal({"deserializer.result"=>{"key"=>1, "key2"=>nil}, "contract.errors.2"=>"screwd", "after_deserialize.fail"=>true, "meantime"=>true, "after_meantime.left?"=>true})
72
- end
73
-
74
- #---
75
- # return value is new input.
76
- it do
77
- pipe = Pipetree::Flow.new [
78
- Pipetree::Flow::On.new(Pipetree::Flow::Right, ->(last, value, options) { [Pipetree::Flow::Right, value.reverse] } )
79
- ]
80
- pipe.("Hello", {}).must_equal [Pipetree::Flow::Right, "olleH"]
81
- end
82
-
83
- #---
84
- # #>
85
- describe "#>" do
86
- let (:pipe) { Pipetree::Flow.new }
87
- it {
88
- pipe.> ->(input, options) { input.reverse }
89
- # pipe.| B
90
- # pipe.% A
91
- pipe.("Hallo", {}).must_equal [Pipetree::Flow::Right, "Hallo"]
92
- }
93
- end
94
-
95
- # #>>
96
- describe "#>>" do
97
- let (:pipe) { Pipetree::Flow.new }
98
- it {
99
- pipe.>> ->(input, options) { input.reverse }
100
- pipe.("Hallo", {}).must_equal [Pipetree::Flow::Right, "ollaH"]
101
- }
102
- end
103
-
104
- #---
105
- # #inspect
106
- Seventeen = ->(*) { snippet }
107
- Long = ->(*) { snippet }
108
- Callable = Object.new # random callable object.
109
-
110
- describe "#inspect" do
111
- let (:pipe) { Pipetree::Flow.new.&(Aaa).>>(Long).<(B).%(Aaa).<(Seventeen).>(Long).>(Callable) }
112
-
113
- it { pipe.inspect.must_equal %{[&Aaa,>>Long,<B,%Aaa,<Seventeen,>Long,>#<Object:>]} }
114
-
115
- it { pipe.inspect(style: :rows).must_equal %{
116
- 0 ==================================&Aaa
117
- 1 ================================>>Long
118
- 2 <B====================================
119
- 3 =================%Aaa=================
120
- 4 <Seventeen============================
121
- 5 =================================>Long
122
- 6 ===========================>#<Object:>} }
123
- end
124
-
125
- describe "#index" do
126
- let (:pipe) { Pipetree::Flow.new.&(Aaa).<(B).%(Aaa, name: "a.triple") }
127
-
128
- it { pipe.index(B).must_equal 1 }
129
- it { pipe.index(Aaa).must_equal 0 }
130
- # with alias
131
- it { pipe.index("a.triple").must_equal 2 }
132
-
133
- # without steps
134
- it { Pipetree::Flow.new.index(B).must_equal nil }
135
- end
136
-
137
- #---
138
- # with aliases
139
- it do
140
- pipe = Pipetree::Flow.new.
141
- >(Aaa, name: "pipe.aaa").
142
- >(B, name: "pipe.b").
143
- >(Aaa, name: "pipe.aaa.aaa")
144
-
145
- pipe.inspect.must_equal %{[>pipe.aaa,>pipe.b,>pipe.aaa.aaa]}
146
- pipe.inspect(style: :rows).must_equal %{
147
- 0 =============================>pipe.aaa
148
- 1 ===============================>pipe.b
149
- 2 =========================>pipe.aaa.aaa}
150
-
151
- pipe.>(Long, after: "pipe.b").inspect.must_equal %{[>pipe.aaa,>pipe.b,>Long,>pipe.aaa.aaa]}
152
- end
153
-
154
- #---
155
- # test decompose array
156
- it do
157
- pipe = Pipetree::Flow.new.
158
- >>( ->(input, options) { [options[:key], input] } ). # passes [bla, input] as input.
159
- &( ->((value, input), options) { input["x"] = value } ) # decomposes input.
160
-
161
- pipe.(input={}, options={key: 1}).must_equal [Pipetree::Flow::Right, [1, {"x"=>1}]]
162
- input.inspect.must_equal %{{"x"=>1}}
163
- options.inspect.must_equal %{{:key=>1}}
164
- end
165
- end
166
-
167
- #- StepMap
168
- class StepMapTest < Minitest::Spec
169
- it do
170
- map = Pipetree::Flow::StepMap.new
171
-
172
- original_proc = ->(*) { snippet }
173
- step = ->(*) { original_proc }
174
-
175
- original_proc2 = ->(*) { snippet }
176
- step2 = ->(*) { original_proc2 }
177
-
178
- map[step] = "my.step", original_proc, ">"
179
- map[step2] = "my.step2", original_proc2, ">"
180
-
181
- map.find_proc(original_proc).must_equal step
182
- map.find_proc("my.step").must_equal step
183
- map.find_proc(original_proc2).must_equal step2
184
- map.find_proc("my.step2").must_equal step2
185
- end
186
- end
187
-
188
- # TODO: instead of testing #index, test all options like :before, etc.
@@ -1,213 +0,0 @@
1
- require "test_helper"
2
-
3
- class PipelineTest < MiniTest::Spec
4
- Song = Struct.new(:title, :artist)
5
- Artist = Struct.new(:name)
6
- Album = Struct.new(:ratings, :artists)
7
-
8
-
9
-
10
- Getter = ->(input, options) { "Yo" }
11
- StopOnNil = ->(input, options) { input }
12
- SkipRender = ->(input, *) { input == "Yo" ? input : P::Stop }
13
-
14
- Prepare = ->(input, options) { "Prepare(#{input})" }
15
- Deserialize = ->(input, options) { "Deserialize(#{input}, #{options[:fragment]})" }
16
-
17
- SkipParse = ->(input, options) { input }
18
- CreateObject = ->(input, options) { OpenStruct.new }
19
-
20
-
21
- Setter = ->(input, options) { "Setter(#{input})" }
22
-
23
- AssignFragment = ->(input, options) { options[:fragment] = input }
24
-
25
- it "linear" do
26
- Pipetree[SkipParse, Setter].("doc", {fragment: 1}).must_equal "Setter(doc)"
27
-
28
-
29
- # parse style.
30
- Pipetree[AssignFragment, SkipParse, CreateObject, Prepare].("Bla", {}).must_equal "Prepare(#<OpenStruct>)"
31
-
32
-
33
- # render style.
34
- Pipetree[Getter, StopOnNil, SkipRender, Prepare, Setter].(nil, {}).
35
- must_equal "Setter(Prepare(Yo))"
36
-
37
- # pipeline = Representable::Pipeline[SkipParse , SetResult, ModifyResult]
38
- # pipeline.(fragment: "yo!").must_equal "modified object from yo!"
39
- end
40
-
41
- Stopping = ->(input, options) { return Pipetree::Stop if options[:fragment] == "stop!"; input }
42
-
43
-
44
- it "stopping" do
45
- pipeline = Pipetree[SkipParse, Stopping, Prepare]
46
- pipeline.(nil, fragment: "oy!").must_equal "Prepare()"
47
- pipeline.(nil, fragment: "stop!").must_equal Pipetree::Stop
48
- end
49
-
50
- describe "Collect" do
51
- Reverse = ->(input, options) { input.reverse }
52
- Add = ->(input, options) { "#{input}+" }
53
- let(:pipeline) { Pipetree::Collect[Reverse, Add] }
54
-
55
- it { pipeline.(["yo!", "oy!"], {}).must_equal ["!oy+", "!yo+"] }
56
-
57
- describe "Pipeline with Collect" do
58
- let(:pipeline) { Pipetree[Reverse, Pipetree::Collect[Reverse, Add]] }
59
- it { pipeline.(["yo!", "oy!"], {}).must_equal ["!yo+", "!oy+"] }
60
- end
61
- end
62
-
63
- #---
64
- # >1 arguments
65
-
66
- First = ->(input, options) { input }
67
- Second = ->(input, options, memory) { input }
68
-
69
- it do
70
- skip
71
- Pipetree[First, Second].("", options={}, memory={})
72
- end
73
-
74
- #---
75
- #- #index
76
- it do
77
- A = ->(*) { snippet }
78
- B = ->(*) { snippet }
79
-
80
- pipe = Pipetree[A, B]
81
- pipe.index(A).must_equal 0
82
- pipe.index(B).must_equal 1
83
- end
84
-
85
- # ######### collection :ratings
86
-
87
- # let (:ratings) {
88
- # dfn = R::Definition.new(:ratings, collection: true, skip_render: ->(*) { false })
89
-
90
- # R::Hash::Binding::Collection.new(dfn)
91
- # }
92
- # it "render scalar collection" do
93
- # doc = {}
94
- # P[
95
- # R::GetValue,
96
- # R::StopOnSkipable,
97
- # R::Collect[
98
- # R::SkipRender,
99
- # ],
100
- # R::AssignName,
101
- # R::WriteFragment
102
- # ].extend(P::Debug).(nil, {represented: Album.new([1,2,3]), binding: ratings, doc: doc, options: {}}).must_equal([1,2,3])
103
-
104
- # doc.must_equal({"ratings"=>[1,2,3]})
105
- # end
106
-
107
- # ######### collection :songs, extend: SongRepresenter
108
- # let (:artists) {
109
- # dfn = R::Definition.new(:artists, collection: true, extend: ArtistRepresenter, class: Artist)
110
-
111
- # R::Hash::Binding::Collection.new(dfn)
112
- # }
113
- # it "render typed collection" do
114
- # doc = {}
115
- # P[
116
- # R::GetValue,
117
- # R::StopOnSkipable,
118
- # R::Collect[
119
- # R::Decorate,
120
- # R::Serialize,
121
- # ],
122
- # R::AssignName,
123
- # R::WriteFragment
124
- # ].extend(P::Debug).(nil, {represented: Album.new(nil, [Artist.new("Diesel Boy"), Artist.new("Van Halen")]), binding: artists, doc: doc, options: {}}).must_equal([{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}])
125
-
126
- # doc.must_equal({"artists"=>[{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}]})
127
- # end
128
-
129
- # let (:album_model) { Album.new(nil, [Artist.new("Diesel Boy"), Artist.new("Van Halen")]) }
130
-
131
- # it "parse typed collection" do
132
- # doc = {"artists"=>[{"name"=>"Diesel Boy"}, {"name"=>"Van Halen"}]}
133
- # P[
134
- # R::AssignName,
135
- # R::ReadFragment,
136
- # R::StopOnNotFound,
137
- # R::OverwriteOnNil,
138
- # # R::SkipParse,
139
- # R::Collect[
140
- # R::AssignFragment,
141
- # R::CreateObject::Class,
142
- # R::Decorate,
143
- # R::Deserialize,
144
- # ],
145
- # R::SetValue,
146
- # ].extend(P::Debug).(doc, {represented: album_model, binding: artists, doc: doc, options: {}}).must_equal([Artist.new("Diesel Boy"), Artist.new("Van Halen")])
147
-
148
- # album_model.artists.must_equal([Artist.new("Diesel Boy"), Artist.new("Van Halen")])
149
- # end
150
-
151
- # # TODO: test with arrays, too, not "only" Pipeline instances.
152
- # describe "#Insert Pipeline[], Function, replace: OldFunction" do
153
- # let (:pipeline) { P[R::GetValue, R::StopOnSkipable, R::StopOnNil] }
154
-
155
- # it "returns Pipeline instance when passing in Pipeline instance" do
156
- # P::Insert.(pipeline, R::Default, replace: R::StopOnSkipable).must_be_instance_of(R::Pipeline)
157
- # end
158
-
159
- # it "replaces if exists" do
160
- # # pipeline.insert!(R::Default, replace: R::StopOnSkipable)
161
-
162
-
163
- # P::Insert.(pipeline, R::Default, replace: R::StopOnSkipable).must_equal P[R::GetValue, R::Default, R::StopOnNil]
164
- # pipeline.must_equal P[R::GetValue, R::StopOnSkipable, R::StopOnNil]
165
- # end
166
-
167
- # it "replaces Function instance" do
168
- # pipeline = P[R::Prepare, R::StopOnSkipable, R::StopOnNil]
169
- # P::Insert.(pipeline, R::Default, replace: R::Prepare).must_equal P[R::Default, R::StopOnSkipable, R::StopOnNil]
170
- # pipeline.must_equal P[R::Prepare, R::StopOnSkipable, R::StopOnNil]
171
- # end
172
-
173
- # it "does not replace when not existing" do
174
- # P::Insert.(pipeline, R::Default, replace: R::Prepare)
175
- # pipeline.must_equal P[R::GetValue, R::StopOnSkipable, R::StopOnNil]
176
- # end
177
-
178
- # it "applies on nested Collect" do
179
- # pipeline = P[R::GetValue, R::Collect[R::GetValue, R::StopOnSkipable], R::StopOnNil]
180
-
181
- # P::Insert.(pipeline, R::Default, replace: R::StopOnSkipable).extend(P::Debug).inspect.must_equal "Pipeline[GetValue, Collect[GetValue, Default], StopOnNil]"
182
- # pipeline.must_equal P[R::GetValue, R::Collect[R::GetValue, R::StopOnSkipable], R::StopOnNil]
183
-
184
-
185
- # P::Insert.(pipeline, R::Default, replace: R::StopOnNil).extend(P::Debug).inspect.must_equal "Pipeline[GetValue, Collect[GetValue, StopOnSkipable], Default]"
186
- # end
187
-
188
- # it "applies on nested Collect with Function::CreateObject" do
189
- # pipeline = P[R::GetValue, R::Collect[R::GetValue, R::CreateObject], R::StopOnNil]
190
-
191
- # P::Insert.(pipeline, R::Default, replace: R::CreateObject).extend(P::Debug).inspect.must_equal "Pipeline[GetValue, Collect[GetValue, Default], StopOnNil]"
192
- # pipeline.must_equal P[R::GetValue, R::Collect[R::GetValue, R::CreateObject], R::StopOnNil]
193
- # end
194
- # end
195
-
196
- # describe "Insert.(delete: true)" do
197
- # let(:pipeline) { P[R::GetValue, R::StopOnNil] }
198
-
199
- # it do
200
- # P::Insert.(pipeline, R::GetValue, delete: true).extend(P::Debug).inspect.must_equal "Pipeline[StopOnNil]"
201
- # pipeline.extend(P::Debug).inspect.must_equal "Pipeline[GetValue, StopOnNil]"
202
- # end
203
- # end
204
-
205
- # describe "Insert.(delete: true) with Collect" do
206
- # let(:pipeline) { P[R::GetValue, R::Collect[R::GetValue, R::StopOnSkipable], R::StopOnNil] }
207
-
208
- # it do
209
- # P::Insert.(pipeline, R::GetValue, delete: true).extend(P::Debug).inspect.must_equal "Pipeline[Collect[StopOnSkipable], StopOnNil]"
210
- # pipeline.extend(P::Debug).inspect.must_equal "Pipeline[GetValue, Collect[GetValue, StopOnSkipable], StopOnNil]"
211
- # end
212
- # end
213
- end