pipetree 0.0.5 → 0.1.0

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 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