trailblazer-operation 0.0.13 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -3
  3. data/CHANGES.md +44 -0
  4. data/Gemfile +13 -2
  5. data/Rakefile +8 -6
  6. data/lib/trailblazer/operation.rb +86 -12
  7. data/lib/trailblazer/operation/deprecated_macro.rb +19 -0
  8. data/lib/trailblazer/operation/inject.rb +34 -0
  9. data/lib/trailblazer/operation/inspect.rb +80 -0
  10. data/lib/trailblazer/operation/public_call.rb +62 -0
  11. data/lib/trailblazer/operation/railway.rb +32 -0
  12. data/lib/trailblazer/operation/railway/fast_track.rb +13 -0
  13. data/lib/trailblazer/operation/railway/normalizer.rb +73 -0
  14. data/lib/trailblazer/operation/railway/task_builder.rb +44 -0
  15. data/lib/trailblazer/operation/result.rb +6 -4
  16. data/lib/trailblazer/operation/skill.rb +8 -24
  17. data/lib/trailblazer/operation/task_wrap.rb +68 -0
  18. data/lib/trailblazer/operation/trace.rb +49 -0
  19. data/lib/trailblazer/operation/variable_mapping.rb +91 -0
  20. data/lib/trailblazer/operation/version.rb +1 -1
  21. data/test/call_test.rb +27 -8
  22. data/test/class_dependencies_test.rb +16 -0
  23. data/test/docs/doormat_test.rb +189 -0
  24. data/test/docs/wiring_test.rb +421 -0
  25. data/test/dry_container_test.rb +4 -0
  26. data/test/fast_track_test.rb +197 -0
  27. data/test/gemfiles/Gemfile.ruby-2.0 +1 -2
  28. data/test/gemfiles/Gemfile.ruby-2.0.lock +40 -0
  29. data/test/inheritance_test.rb +1 -1
  30. data/test/inspect_test.rb +43 -0
  31. data/test/introspect_test.rb +50 -0
  32. data/test/macro_test.rb +61 -0
  33. data/test/operation_test.rb +94 -0
  34. data/test/result_test.rb +14 -8
  35. data/test/ruby-2.0.0/operation_test.rb +73 -0
  36. data/test/ruby-2.0.0/step_test.rb +136 -0
  37. data/test/skill_test.rb +66 -48
  38. data/test/step_test.rb +228 -0
  39. data/test/task_wrap_test.rb +91 -0
  40. data/test/test_helper.rb +37 -0
  41. data/test/trace_test.rb +62 -0
  42. data/test/variable_mapping_test.rb +66 -0
  43. data/test/wire_test.rb +113 -0
  44. data/test/wiring/defaults_test.rb +197 -0
  45. data/test/wiring/subprocess_test.rb +70 -0
  46. data/trailblazer-operation.gemspec +3 -5
  47. metadata +62 -36
  48. data/lib/trailblazer/operation/1.9.3/option.rb +0 -36
  49. data/lib/trailblazer/operation/generic.rb +0 -12
  50. data/lib/trailblazer/operation/option.rb +0 -54
  51. data/lib/trailblazer/operation/pipetree.rb +0 -142
  52. data/lib/trailblazer/skill.rb +0 -70
  53. data/test/2.0.0-pipetree_test.rb +0 -100
  54. data/test/2.1.0-pipetree_test.rb +0 -100
  55. data/test/operation_skill_test.rb +0 -89
  56. data/test/pipetree_test.rb +0 -185
@@ -1,70 +0,0 @@
1
- # TODO: mark/make all but mutable_options as frozen.
2
- # The idea of Skill is to have a generic, ordered read/write interface that
3
- # collects mutable runtime-computed data while providing access to compile-time
4
- # information.
5
- # The runtime-data takes precedence over the class data.
6
- module Trailblazer
7
- class Skill
8
- def initialize(*containers)
9
- @mutable_options = {}
10
- @resolver = Resolver.new(@mutable_options, *containers)
11
- end
12
-
13
- def [](name)
14
- @resolver[name]
15
- end
16
-
17
- def []=(name, value)
18
- @mutable_options[name] = value
19
- end
20
-
21
- # THIS METHOD IS CONSIDERED PRIVATE AND MIGHT BE REMOVED.
22
- # Options from ::call (e.g. "user.current"), containers, etc.
23
- # NO mutual data from the caller operation. no class state.
24
- def to_runtime_data
25
- @resolver.instance_variable_get(:@containers).slice(1..-2)
26
- end
27
-
28
- # THIS METHOD IS CONSIDERED PRIVATE AND MIGHT BE REMOVED.
29
- def to_mutable_data
30
- @mutable_options
31
- end
32
-
33
- # Called when Ruby transforms options into kw args, via **options.
34
- # TODO: this method has massive potential for speed improvements.
35
- # The `tmp_options` argument is experimental. It allows adding temporary options
36
- # to the kw args.
37
- #:private:
38
- def to_hash(tmp_options={})
39
- {}.tap do |h|
40
- arr = to_runtime_data << to_mutable_data << tmp_options
41
-
42
- arr.each { |hsh|
43
- hsh.each { |k, v| h[k.to_sym] = v }
44
- }
45
- end
46
- end
47
-
48
- # Look through a list of containers until you find the skill.
49
- class Resolver
50
- # alternative implementation:
51
- # containers.reverse.each do |container| @mutable_options.merge!(container) end
52
- #
53
- # benchmark, merging in #initialize vs. this resolver.
54
- # merge 39.678k (± 9.1%) i/s - 198.700k in 5.056653s
55
- # resolver 68.928k (± 6.4%) i/s - 342.836k in 5.001610s
56
- def initialize(*containers)
57
- @containers = containers
58
- end
59
-
60
- def [](name)
61
- @containers.find { |container| container.key?(name) && (return container[name]) }
62
- end
63
- end
64
-
65
- # private API.
66
- def inspect
67
- "<Skill #{@resolver.instance_variable_get(:@containers).collect { |c| c.inspect }.join(" ")}>"
68
- end
69
- end
70
- end
@@ -1,100 +0,0 @@
1
- require "test_helper"
2
-
3
- class KWBugsTest < Minitest::Spec
4
- Merchant = Struct.new(:id)
5
-
6
- class Merchant::New < Trailblazer::Operation
7
- step ->(options) {
8
- options["model"] = 1
9
- options["bla"] = true # this breaks Ruby 2.2.2.
10
- }
11
-
12
- step :add!
13
- def add!(yo:nil, model:nil, **)
14
- raise if model.nil?
15
- end
16
- end
17
-
18
- it { Merchant::New.( {}, { "yo"=> nil } ) }
19
- end
20
-
21
- class KWOptionsTest < Minitest::Spec
22
- X = Trailblazer::Operation::Option::KW.( ->(options, **) { options["x"] = true } )
23
- Y = Trailblazer::Operation::Option::KW.( ->(options, params:nil, **) { options["y"] = params } )
24
- Z = Trailblazer::Operation::Option::KW.( ->(options, params:nil, z:nil, **) { options["kw_z"] = z } )
25
- A = Trailblazer::Operation::Option::KW.( ->(options, params:nil, z:nil, **) { options["options_z"] = options["z"] } )
26
-
27
- class Create < Trailblazer::Operation
28
- step [ ->(input, options) { X.(input, options, z: "Z!") }, name: "X" ]
29
- step [ ->(input, options) { Y.(input, options, z: "Z!") }, name: "Y" ]
30
- step [ ->(input, options) { Z.(input, options, z: "Z!") }, name: "Z" ]
31
- step [ ->(input, options) { A.(input, options, z: "Z!") }, name: "A" ]
32
- end
33
-
34
- it { Create.({ params: "yo" }, "z" => 1).inspect("x", "y", "kw_z", "options_z").must_equal %{<Result:true [true, {:params=>\"yo\"}, "Z!", 1] >} }
35
- end
36
-
37
-
38
- class Ruby200PipetreeTest < Minitest::Spec
39
- class Create < Trailblazer::Operation
40
- step ->(*, params:nil, **) { params["run"] } # only test kws.
41
- step ->(options, params:nil, **) { options["x"] = params["run"] } # read and write.
42
- step ->(options) { options["y"] = options["params"]["run"] } # old API.
43
- end
44
-
45
- it { Create.("run" => false).inspect("x", "y").must_equal %{<Result:false [nil, nil] >} }
46
- it { Create.("run" => true).inspect("x", "y").must_equal %{<Result:true [true, true] >} }
47
-
48
- #- instance methods
49
- class Update < Trailblazer::Operation
50
- step :params! # only test kws.
51
- step :x! # read and write.
52
- step :y! # old API.
53
-
54
- def params!(*, params:nil, **)
55
- params["run"]
56
- end
57
-
58
- def x!(options, params:nil, **)
59
- options["x"] = params["run"]
60
- end
61
-
62
- def y!(options)
63
- options["y"] = options["params"]["run"]
64
- end
65
- end
66
-
67
- it { Update.("run" => false).inspect("x", "y").must_equal %{<Result:false [nil, nil] >} }
68
- it { Update.("run" => true).inspect("x", "y").must_equal %{<Result:true [true, true] >} }
69
-
70
-
71
- class Delete < Trailblazer::Operation
72
- class Params
73
- extend Uber::Callable
74
- def self.call(*, params:nil, **)
75
- params["run"]
76
- end
77
- end
78
-
79
- class X
80
- extend Uber::Callable
81
- def self.call(options, params:nil, **)
82
- options["x"] = params["run"]
83
- end
84
- end
85
-
86
- class Y
87
- extend Uber::Callable
88
- def self.call(options)
89
- options["y"] = options["params"]["run"]
90
- end
91
- end
92
-
93
- step Params
94
- step X
95
- step Y
96
- end
97
-
98
- it { Delete.("run" => false).inspect("x", "y").must_equal %{<Result:false [nil, nil] >} }
99
- it { Delete.("run" => true).inspect("x", "y").must_equal %{<Result:true [true, true] >} }
100
- end
@@ -1,100 +0,0 @@
1
- require "test_helper"
2
-
3
- class KWBugsTest < Minitest::Spec
4
- Merchant = Struct.new(:id)
5
-
6
- class Merchant::New < Trailblazer::Operation
7
- step ->(options) {
8
- options["model"] = 1
9
- options["bla"] = true # this breaks Ruby 2.2.2.
10
- }
11
-
12
- step :add!
13
- def add!(yo:, model:, **)
14
- raise if model.nil?
15
- end
16
- end
17
-
18
- it { Merchant::New.( {}, { "yo"=> nil } ) }
19
- end
20
-
21
- class KWOptionsTest < Minitest::Spec
22
- X = Trailblazer::Operation::Option::KW.( ->(options, **) { options["x"] = true } )
23
- Y = Trailblazer::Operation::Option::KW.( ->(options, params:, **) { options["y"] = params } )
24
- Z = Trailblazer::Operation::Option::KW.( ->(options, params:, z:, **) { options["kw_z"] = z } )
25
- A = Trailblazer::Operation::Option::KW.( ->(options, params:, z:, **) { options["options_z"] = options["z"] } )
26
-
27
- class Create < Trailblazer::Operation
28
- step [ ->(input, options) { X.(input, options, z: "Z!") }, name: "X" ]
29
- step [ ->(input, options) { Y.(input, options, z: "Z!") }, name: "Y" ]
30
- step [ ->(input, options) { Z.(input, options, z: "Z!") }, name: "Z" ]
31
- step [ ->(input, options) { A.(input, options, z: "Z!") }, name: "A" ]
32
- end
33
-
34
- it { Create.({ params: "yo" }, "z" => 1).inspect("x", "y", "kw_z", "options_z").must_equal %{<Result:true [true, {:params=>\"yo\"}, "Z!", 1] >} }
35
- end
36
-
37
-
38
- class Ruby200PipetreeTest < Minitest::Spec
39
- class Create < Trailblazer::Operation
40
- step ->(*, params:, **) { params["run"] } # only test kws.
41
- step ->(options, params:nil, **) { options["x"] = params["run"] } # read and write.
42
- step ->(options) { options["y"] = options["params"]["run"] } # old API.
43
- end
44
-
45
- it { Create.("run" => false).inspect("x", "y").must_equal %{<Result:false [nil, nil] >} }
46
- it { Create.("run" => true).inspect("x", "y").must_equal %{<Result:true [true, true] >} }
47
-
48
- #- instance methods
49
- class Update < Trailblazer::Operation
50
- step :params! # only test kws.
51
- step :x! # read and write.
52
- step :y! # old API.
53
-
54
- def params!(*, params:, **)
55
- params["run"]
56
- end
57
-
58
- def x!(options, params:nil, **)
59
- options["x"] = params["run"]
60
- end
61
-
62
- def y!(options)
63
- options["y"] = options["params"]["run"]
64
- end
65
- end
66
-
67
- it { Update.("run" => false).inspect("x", "y").must_equal %{<Result:false [nil, nil] >} }
68
- it { Update.("run" => true).inspect("x", "y").must_equal %{<Result:true [true, true] >} }
69
-
70
-
71
- class Delete < Trailblazer::Operation
72
- class Params
73
- extend Uber::Callable
74
- def self.call(*, params:, **)
75
- params["run"]
76
- end
77
- end
78
-
79
- class X
80
- extend Uber::Callable
81
- def self.call(options, params:nil, **)
82
- options["x"] = params["run"]
83
- end
84
- end
85
-
86
- class Y
87
- extend Uber::Callable
88
- def self.call(options)
89
- options["y"] = options["params"]["run"]
90
- end
91
- end
92
-
93
- step Params
94
- step X
95
- step Y
96
- end
97
-
98
- it { Delete.("run" => false).inspect("x", "y").must_equal %{<Result:false [nil, nil] >} }
99
- it { Delete.("run" => true).inspect("x", "y").must_equal %{<Result:true [true, true] >} }
100
- end
@@ -1,89 +0,0 @@
1
- require "test_helper"
2
-
3
- class OperationSkillTest < Minitest::Spec
4
- class Create < Trailblazer::Operation
5
- end
6
-
7
- # no dependencies provided.
8
- it { Create.()[:not_existent].must_equal nil }
9
- # dependencies provided.
10
- it { Create.({}, contract: Object)[:not_existent].must_equal nil }
11
- it { Create.({}, contract: Object)[:contract].must_equal Object }
12
- end
13
-
14
- class OperationCompetenceTest < Minitest::Spec
15
- # Operation#[]
16
- # Operation#[]=
17
- # arbitrary options can be saved via Op#[].
18
- class Create < Trailblazer::Operation
19
- success :call
20
-
21
- def call(*)
22
- self["drink"] = "Little Creatures"
23
- self["drink"]
24
- end
25
- end
26
-
27
- it { Create.()["drink"].must_equal "Little Creatures" }
28
- # instance can override constructor options.
29
- it { Create.({}, "drink" => "Four Pines")["drink"].must_equal "Little Creatures" }
30
- # original hash doesn't get changed.
31
- it do
32
- Create.({}, hash = { "drink" => "Four Pines" })
33
- hash.must_equal( { "drink" => "Four Pines" })
34
- end
35
-
36
-
37
- # Operation::[]
38
- # Operation::[]=
39
- class Update < Trailblazer::Operation
40
- success :call
41
-
42
- self["drink"] = "Beer"
43
-
44
- def call(*)
45
- self["drink"]
46
- end
47
- end
48
-
49
- it { Update["drink"].must_equal "Beer" }
50
-
51
- # class-level are available on instance-level via Op#[]
52
- it { Update.()["drink"].must_equal "Beer" }
53
-
54
- # runtime constructor options can override class-level.
55
- it { Update.({}, "drink" => "Little Creatures")["drink"].must_equal "Little Creatures" }
56
-
57
- # instance can override class-level
58
- class Delete < Trailblazer::Operation
59
- success :call
60
-
61
- self["drink"] = "Beer"
62
-
63
- def call(*)
64
- self["drink"] = "Little Creatures"
65
- self["drink"]
66
- end
67
- end
68
-
69
- # Op#[]= can override class-level...
70
- it { Delete.()["drink"].must_equal "Little Creatures" }
71
- # ...but it doesn't change class-level.
72
- it { Delete["drink"].must_equal "Beer" }
73
-
74
- # we don't really need this test.
75
- class Reward < Trailblazer::Operation
76
- self["drink"] = "Beer"
77
- end
78
-
79
- it { Reward.()["drink"].must_equal "Beer" }
80
- end
81
-
82
- # {
83
- # user_repository: ..,
84
- # current_user: ..,
85
- # }
86
-
87
-
88
- # 1. initialize(params, {})
89
- # 2. extend AutoInject[] shouldn't override constructor but simply pass injected dependencies in second arg (by adding dependencies to hash).
@@ -1,185 +0,0 @@
1
- require "test_helper"
2
-
3
- class PipetreeTest < Minitest::Spec
4
- def self.Validate
5
- step = ->{ snippet }
6
-
7
- [ step, name: "validate", before: "operation.new" ]
8
- end
9
-
10
- # without options
11
- class Create < Trailblazer::Operation
12
- step PipetreeTest::Validate()
13
- step PipetreeTest::Validate(), name: "VALIDATE!"
14
- end
15
-
16
- it { Create["pipetree"].inspect.must_equal %{[>validate,>VALIDATE!,>operation.new]} }
17
-
18
-
19
-
20
- # with options
21
- class Update < Trailblazer::Operation
22
- step PipetreeTest::Validate(), after: "operation.new"
23
- end
24
-
25
- it { Update["pipetree"].inspect.must_equal %{[>operation.new,>validate]} }
26
-
27
- #---
28
- # ::step
29
-
30
- #-
31
- #- with :symbol
32
- class Delete < Trailblazer::Operation
33
- step :call!
34
-
35
- def call!(options)
36
- self["x"] = options["params"]
37
- end
38
- end
39
-
40
- it { Delete.("yo")["x"].must_equal "yo" }
41
-
42
- #- inheritance
43
- class Remove < Delete
44
- end
45
-
46
- it { Remove.("yo")["x"].must_equal "yo" }
47
-
48
- # proc arguments
49
- class Forward < Trailblazer::Operation
50
- step ->(input, options) { puts "@@@@@ #{input.inspect}"; puts "@@@@@ #{options.inspect}" }
51
- end
52
-
53
- it { skip; Forward.({ id: 1 }) }
54
-
55
- #---
56
- # ::>, ::<, ::>>, :&
57
- # with proc, method, callable.
58
- class Right < Trailblazer::Operation
59
- MyProc = ->(*) { }
60
- success ->(options) { options[">"] = options["params"][:id] }, better_api: true
61
-
62
- success :method_name!
63
- def method_name!(options); self["method_name!"] = options["params"][:id] end
64
-
65
- class MyCallable
66
- include Uber::Callable
67
- def call(options); options["callable"] = options["params"][:id] end
68
- end
69
- success MyCallable.new
70
- end
71
-
72
- it { Right.( id: 1 ).slice(">", "method_name!", "callable").must_equal [1, 1, 1] }
73
- it { Right["pipetree"].inspect.must_equal %{[>operation.new,>pipetree_test.rb:60,>method_name!,>PipetreeTest::Right::MyCallable]} }
74
-
75
- #---
76
- # inheritance
77
- class Righter < Right
78
- success ->(options) { options["righter"] = true }
79
- end
80
-
81
- it { Righter.( id: 1 ).slice(">", "method_name!", "callable", "righter").must_equal [1, 1, 1, true] }
82
- end
83
-
84
-
85
- class FailPassFastOptionTest < Minitest::Spec
86
- # #failure fails fast.
87
- class Create < Trailblazer::Operation
88
- step ->(options, *) { options["x"] = options["dont_fail"] }
89
- failure ->(options, *) { options["a"] = true; options["fail_fast"] }, fail_fast: true
90
- failure ->(options, *) { options["b"] = true }
91
- step ->(options, *) { options["y"] = true }
92
- end
93
-
94
- it { Create.({}, "fail_fast" => true, "dont_fail" => true ).inspect("x", "a", "b", "y").must_equal %{<Result:true [true, nil, nil, true] >} }
95
- it { Create.({}, "fail_fast" => true ).inspect("x", "a", "b", "y").must_equal %{<Result:false [nil, true, nil, nil] >} }
96
- it { Create.({}, "fail_fast" => false ).inspect("x", "a", "b", "y").must_equal %{<Result:false [nil, true, nil, nil] >} }
97
-
98
- # #success passes fast.
99
- class Retrieve < Trailblazer::Operation
100
- success ->(options, *) { options["x"] = options["dont_fail"] }, pass_fast: true
101
- failure ->(options, *) { options["b"] = true }
102
- step ->(options, *) { options["y"] = true }
103
- end
104
-
105
- it { Retrieve.({}, "dont_fail" => true ).inspect("x", "b", "y").must_equal %{<Result:true [true, nil, nil] >} }
106
- it { Retrieve.({}, "dont_fail" => false ).inspect("x", "b", "y").must_equal %{<Result:true [false, nil, nil] >} }
107
-
108
- # #step fails fast if option set and returns false.
109
- class Update < Trailblazer::Operation
110
- step ->(options, *) { options["x"] = true }
111
- step ->(options, *) { options["a"] = options["dont_fail"] }, fail_fast: true # only on false.
112
- failure ->(options, *) { options["b"] = true }
113
- step ->(options, *) { options["y"] = true }
114
- end
115
-
116
- it { Update.({}, "dont_fail" => true).inspect("x", "a", "b", "y").must_equal %{<Result:true [true, true, nil, true] >} }
117
- it { Update.({} ).inspect("x", "a", "b", "y").must_equal %{<Result:false [true, nil, nil, nil] >} }
118
-
119
- # #step passes fast if option set and returns true.
120
- class Delete < Trailblazer::Operation
121
- step ->(options, *) { options["x"] = true }
122
- step ->(options, *) { options["a"] = options["dont_fail"] }, pass_fast: true # only on true.
123
- failure ->(options, *) { options["b"] = true }
124
- step ->(options, *) { options["y"] = true }
125
- end
126
-
127
- it { Delete.({}, "dont_fail" => true).inspect("x", "a", "b", "y").must_equal %{<Result:true [true, true, nil, nil] >} }
128
- it { Delete.({} ).inspect("x", "a", "b", "y").must_equal %{<Result:false [true, nil, true, nil] >} }
129
- end
130
-
131
- class FailBangTest < Minitest::Spec
132
- class Create < Trailblazer::Operation
133
- step ->(options, *) { options["x"] = true; Railway.fail! }
134
- step ->(options, *) { options["y"] = true }
135
- failure ->(options, *) { options["a"] = true }
136
- end
137
-
138
- it { Create.().inspect("x", "y", "a").must_equal %{<Result:false [true, nil, true] >} }
139
- end
140
-
141
- class PassBangTest < Minitest::Spec
142
- class Create < Trailblazer::Operation
143
- step ->(options, *) { options["x"] = true; Railway.pass! }
144
- step ->(options, *) { options["y"] = true }
145
- failure ->(options, *) { options["a"] = true }
146
- end
147
-
148
- it { Create.().inspect("x", "y", "a").must_equal %{<Result:true [true, true, nil] >} }
149
- end
150
-
151
- class FailFastBangTest < Minitest::Spec
152
- class Create < Trailblazer::Operation
153
- step ->(options, *) { options["x"] = true; Railway.fail_fast! }
154
- step ->(options, *) { options["y"] = true }
155
- failure ->(options, *) { options["a"] = true }
156
- end
157
-
158
- it { Create.().inspect("x", "y", "a").must_equal %{<Result:false [true, nil, nil] >} }
159
- end
160
-
161
- class PassFastBangTest < Minitest::Spec
162
- class Create < Trailblazer::Operation
163
- step ->(options, *) { options["x"] = true; Railway.pass_fast! }
164
- step ->(options, *) { options["y"] = true }
165
- failure ->(options, *) { options["a"] = true }
166
- end
167
-
168
- it { Create.().inspect("x", "y", "a").must_equal %{<Result:true [true, nil, nil] >} }
169
- end
170
-
171
-
172
- class OverrideTest < Minitest::Spec
173
- class Create < Trailblazer::Operation
174
- step :a
175
- step :b
176
- end
177
-
178
- class Update < Create
179
- step :a, override: true
180
- end
181
-
182
- # FIXME: also test Macro
183
- it { Create["pipetree"].inspect.must_equal %{[>operation.new,>a,>b]} }
184
- it { Update["pipetree"].inspect.must_equal %{[>operation.new,>a,>b]} }
185
- end