trailblazer-operation 0.0.13 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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