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
@@ -0,0 +1,70 @@
1
+ # require "test_helper"
2
+
3
+ # class WiringWithNestedTest < Minitest::Spec
4
+ # MyEnd = Class.new(Trailblazer::Circuit::End)
5
+
6
+ # class Create < Trailblazer::Operation
7
+ # class Edit < Trailblazer::Operation
8
+ # step :a
9
+
10
+ # def a(options, a_return:, **)
11
+ # options["a"] = 1
12
+ # a_return
13
+ # end
14
+
15
+ # # operations should expose their outputs with proper naming.
16
+ # #
17
+ # # {#<Trailblazer::Operation::Railway::End::Success:0x00000001b22310
18
+ # # @name=:success,
19
+ # # @options={}>=>{:role=>:success},
20
+ # # #<Trailblazer::Operation::Railway::End::Failure:0x00000001b222c0
21
+ # # @name=:failure,
22
+ # # @options={}>=>{:role=>:unauthorized}}
23
+ # def self.outputs
24
+ # # Outputs::Only(super, :success, :failure)
25
+ # # Outputs::Update( outputs, failure: { role: :unauthorized} )
26
+
27
+ # outs = super.to_a
28
+
29
+ # outs = Hash[*(outs[0]+outs[1])]
30
+
31
+ # outs.merge( outs.keys[1] => { role: :unauthorized } )
32
+ # end
33
+ # end
34
+
35
+ # attach MyEnd.new(:myend), id: "End.unauthorized"
36
+
37
+ # step ( { task: Trailblazer::Activity::Subprocess( Edit, call: :__call__ ),
38
+ # node_data: { id: "Nested/" },
39
+ # outputs: Edit.outputs, # THIS SHOULD OVERRIDE.
40
+ # connect_to: Merge({ unauthorized: "End.unauthorized" }) # connects :success automatically.
41
+ # }), fast_track: true
42
+
43
+ # step :b
44
+ # fail :f
45
+
46
+ # def b(options, a:, **)
47
+ # options["b"] = a+1
48
+ # end
49
+
50
+ # def f(options, a:, **)
51
+ # options["f"] = a+2
52
+ # end
53
+ # end
54
+
55
+ # # Edit ==> true, will run :b
56
+ # it do
57
+ # result = Create.({}, a_return: true )
58
+ # result.inspect("a", "b", "f").must_equal %{<Result:true [1, 2, nil] >}
59
+ # result.event.must_be_instance_of Trailblazer::Operation::Railway::End::Success
60
+ # end
61
+
62
+ # # Edit ==> false, will end on End.unauthorized.
63
+ # it do
64
+ # result = Create.({}, a_return: false )
65
+ # result.inspect("a", "b", "f").must_equal %{<Result:false [1, nil, nil] >}
66
+ # result.event.must_be_instance_of MyEnd
67
+
68
+ # # Create.trace({}, a_return: false ).wtf
69
+ # end
70
+ # end
@@ -17,14 +17,12 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
-
21
- spec.add_dependency "uber" # Uber::Callable is all we need.
22
- spec.add_dependency "declarative"
23
- spec.add_dependency "pipetree", ">= 0.1.1", "< 0.2.0"
20
+ spec.add_dependency "trailblazer-activity", ">= 0.3.0", "< 0.4.0"
21
+ spec.add_dependency "trailblazer-context", ">= 0.1.1", "< 0.3.0"
24
22
 
25
23
  spec.add_development_dependency "bundler"
26
24
  spec.add_development_dependency "rake"
27
25
  spec.add_development_dependency "minitest"
28
26
 
29
- spec.required_ruby_version = '>= 1.9.3'
27
+ spec.required_ruby_version = '>= 2.0.0'
30
28
  end
metadata CHANGED
@@ -1,45 +1,37 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailblazer-operation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-12 00:00:00.000000000 Z
11
+ date: 2017-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: uber
14
+ name: trailblazer-activity
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: declarative
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
19
+ version: 0.3.0
20
+ - - "<"
32
21
  - !ruby/object:Gem::Version
33
- version: '0'
22
+ version: 0.4.0
34
23
  type: :runtime
35
24
  prerelease: false
36
25
  version_requirements: !ruby/object:Gem::Requirement
37
26
  requirements:
38
27
  - - ">="
39
28
  - !ruby/object:Gem::Version
40
- version: '0'
29
+ version: 0.3.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 0.4.0
41
33
  - !ruby/object:Gem::Dependency
42
- name: pipetree
34
+ name: trailblazer-context
43
35
  requirement: !ruby/object:Gem::Requirement
44
36
  requirements:
45
37
  - - ">="
@@ -47,7 +39,7 @@ dependencies:
47
39
  version: 0.1.1
48
40
  - - "<"
49
41
  - !ruby/object:Gem::Version
50
- version: 0.2.0
42
+ version: 0.3.0
51
43
  type: :runtime
52
44
  prerelease: false
53
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -57,7 +49,7 @@ dependencies:
57
49
  version: 0.1.1
58
50
  - - "<"
59
51
  - !ruby/object:Gem::Version
60
- version: 0.2.0
52
+ version: 0.3.0
61
53
  - !ruby/object:Gem::Dependency
62
54
  name: bundler
63
55
  requirement: !ruby/object:Gem::Requirement
@@ -114,28 +106,48 @@ files:
114
106
  - README.md
115
107
  - Rakefile
116
108
  - lib/trailblazer/operation.rb
117
- - lib/trailblazer/operation/1.9.3/option.rb
118
- - lib/trailblazer/operation/generic.rb
119
- - lib/trailblazer/operation/option.rb
120
- - lib/trailblazer/operation/pipetree.rb
109
+ - lib/trailblazer/operation/deprecated_macro.rb
110
+ - lib/trailblazer/operation/inject.rb
111
+ - lib/trailblazer/operation/inspect.rb
112
+ - lib/trailblazer/operation/public_call.rb
113
+ - lib/trailblazer/operation/railway.rb
114
+ - lib/trailblazer/operation/railway/fast_track.rb
115
+ - lib/trailblazer/operation/railway/normalizer.rb
116
+ - lib/trailblazer/operation/railway/task_builder.rb
121
117
  - lib/trailblazer/operation/result.rb
122
118
  - lib/trailblazer/operation/skill.rb
119
+ - lib/trailblazer/operation/task_wrap.rb
120
+ - lib/trailblazer/operation/trace.rb
121
+ - lib/trailblazer/operation/variable_mapping.rb
123
122
  - lib/trailblazer/operation/version.rb
124
- - lib/trailblazer/skill.rb
125
- - test/2.0.0-pipetree_test.rb
126
- - test/2.1.0-pipetree_test.rb
127
123
  - test/benchmark/skill_resolver_benchmark.rb
128
124
  - test/call_test.rb
125
+ - test/class_dependencies_test.rb
126
+ - test/docs/doormat_test.rb
127
+ - test/docs/wiring_test.rb
129
128
  - test/dry_container_test.rb
129
+ - test/fast_track_test.rb
130
130
  - test/gemfiles/Gemfile.ruby-1.9
131
131
  - test/gemfiles/Gemfile.ruby-2.0
132
+ - test/gemfiles/Gemfile.ruby-2.0.lock
132
133
  - test/gemfiles/Gemfile.ruby-2.3
133
134
  - test/inheritance_test.rb
134
- - test/operation_skill_test.rb
135
- - test/pipetree_test.rb
135
+ - test/inspect_test.rb
136
+ - test/introspect_test.rb
137
+ - test/macro_test.rb
138
+ - test/operation_test.rb
136
139
  - test/result_test.rb
140
+ - test/ruby-2.0.0/operation_test.rb
141
+ - test/ruby-2.0.0/step_test.rb
137
142
  - test/skill_test.rb
143
+ - test/step_test.rb
144
+ - test/task_wrap_test.rb
138
145
  - test/test_helper.rb
146
+ - test/trace_test.rb
147
+ - test/variable_mapping_test.rb
148
+ - test/wire_test.rb
149
+ - test/wiring/defaults_test.rb
150
+ - test/wiring/subprocess_test.rb
139
151
  - trailblazer-operation.gemspec
140
152
  homepage: http://trailblazer.to
141
153
  licenses:
@@ -149,7 +161,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
149
161
  requirements:
150
162
  - - ">="
151
163
  - !ruby/object:Gem::Version
152
- version: 1.9.3
164
+ version: 2.0.0
153
165
  required_rubygems_version: !ruby/object:Gem::Requirement
154
166
  requirements:
155
167
  - - ">="
@@ -157,22 +169,36 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
169
  version: '0'
158
170
  requirements: []
159
171
  rubyforge_project:
160
- rubygems_version: 2.6.3
172
+ rubygems_version: 2.6.12
161
173
  signing_key:
162
174
  specification_version: 4
163
175
  summary: Trailblazer's operation object with railway flow and integrated error handling.
164
176
  test_files:
165
- - test/2.0.0-pipetree_test.rb
166
- - test/2.1.0-pipetree_test.rb
167
177
  - test/benchmark/skill_resolver_benchmark.rb
168
178
  - test/call_test.rb
179
+ - test/class_dependencies_test.rb
180
+ - test/docs/doormat_test.rb
181
+ - test/docs/wiring_test.rb
169
182
  - test/dry_container_test.rb
183
+ - test/fast_track_test.rb
170
184
  - test/gemfiles/Gemfile.ruby-1.9
171
185
  - test/gemfiles/Gemfile.ruby-2.0
186
+ - test/gemfiles/Gemfile.ruby-2.0.lock
172
187
  - test/gemfiles/Gemfile.ruby-2.3
173
188
  - test/inheritance_test.rb
174
- - test/operation_skill_test.rb
175
- - test/pipetree_test.rb
189
+ - test/inspect_test.rb
190
+ - test/introspect_test.rb
191
+ - test/macro_test.rb
192
+ - test/operation_test.rb
176
193
  - test/result_test.rb
194
+ - test/ruby-2.0.0/operation_test.rb
195
+ - test/ruby-2.0.0/step_test.rb
177
196
  - test/skill_test.rb
197
+ - test/step_test.rb
198
+ - test/task_wrap_test.rb
178
199
  - test/test_helper.rb
200
+ - test/trace_test.rb
201
+ - test/variable_mapping_test.rb
202
+ - test/wire_test.rb
203
+ - test/wiring/defaults_test.rb
204
+ - test/wiring/subprocess_test.rb
@@ -1,36 +0,0 @@
1
- class Trailblazer::Operation
2
- # :private:
3
- module Option
4
- def self.call(proc, &block)
5
- type = :proc
6
-
7
- option =
8
- if proc.is_a? Symbol
9
- type = :symbol
10
- ->(input, _options) { call_method(proc, input, _options) }
11
- elsif proc.is_a? Proc
12
- ->(input, _options) { call_proc(proc, input, _options) }
13
- elsif proc.is_a? Uber::Callable
14
- type = :callable
15
- ->(input, _options) { call_callable(proc, input, _options) }
16
- end
17
-
18
- yield type if block_given?
19
- option
20
- end
21
-
22
- def self.call_proc(proc, input, options)
23
- proc.(options)
24
- end
25
-
26
- def self.call_method(proc, input, options)
27
- input.send(proc, options)
28
- end
29
-
30
- def self.call_callable(callable, input, options)
31
- callable.(options)
32
- end
33
-
34
- KW = Option
35
- end # Option
36
- end
@@ -1,12 +0,0 @@
1
- module Trailblazer
2
- # Generic initializer for the operation.
3
- module Operation::Generic
4
- def initialize(skills={})
5
- @skills = skills
6
- end
7
-
8
- # dependency interface
9
- extend Forwardable
10
- def_delegators :@skills, :[], :[]=
11
- end
12
- end
@@ -1,54 +0,0 @@
1
- class Trailblazer::Operation
2
- # :private:
3
- # This code is not beautiful, but could also be worse.
4
- # I'm expecting some of this to go to Uber, as we use this pattern everywhere.
5
- class Option
6
- def self.call(proc, &block)
7
- type = :proc
8
-
9
- option =
10
- if proc.is_a? Symbol
11
- type = :symbol
12
- ->(input, *_options) { call_method(proc, input, *_options) }
13
- elsif proc.is_a? Proc
14
- ->(input, *_options) { call_proc(proc, input, *_options) }
15
- elsif proc.is_a? Uber::Callable
16
- type = :callable
17
- ->(input, *_options) { call_callable(proc, input, *_options) }
18
- end
19
-
20
- yield type if block_given?
21
- option
22
- end
23
-
24
- def self.call_proc(proc, input, *options)
25
- proc.(*options)
26
- end
27
-
28
- def self.call_method(proc, input, *options)
29
- input.send(proc, *options)
30
- end
31
-
32
- def self.call_callable(callable, input, *options)
33
- callable.(*options)
34
- end
35
-
36
- # Call the option with keyword arguments. Ruby <= 2.0.
37
- class KW < Option
38
- def self.call_proc(proc, input, options, tmp_options={})
39
- return proc.(options) if proc.arity == 1
40
- proc.(options, **options.to_hash(tmp_options))
41
- end
42
-
43
- def self.call_method(proc, input, options, tmp_options={})
44
- return input.send(proc, options) if input.method(proc).arity == 1 # TODO: remove this
45
- input.send(proc, options, **options.to_hash(tmp_options))
46
- end
47
-
48
- def self.call_callable(callable, input, options, tmp_options={})
49
- return callable.(options) if callable.method(:call).arity == 1
50
- callable.(options, **options.to_hash(tmp_options))
51
- end
52
- end
53
- end
54
- end
@@ -1,142 +0,0 @@
1
- require "pipetree"
2
- require "pipetree/railway"
3
- require "trailblazer/operation/result"
4
-
5
- if RUBY_VERSION == "1.9.3"
6
- require "trailblazer/operation/1.9.3/option" # TODO: rename to something better.
7
- else
8
- require "trailblazer/operation/option" # TODO: rename to something better.
9
- end
10
-
11
- class Trailblazer::Operation
12
- Instantiate = ->(klass, options) { klass.new(options) } # returns operation instance.
13
-
14
- # Implements the API to populate the operation's pipetree and
15
- # `Operation::call` to invoke the latter.
16
- # Learn more about the Pipetree gem here: https://github.com/apotonick/pipetree
17
- module Pipetree
18
- def self.included(includer)
19
- includer.extend ClassMethods # ::call, ::inititalize_pipetree!
20
- includer.extend DSL # ::|, ::> and friends.
21
-
22
- includer.initialize_pipetree!
23
- end
24
-
25
- module ClassMethods
26
- # Top-level, this method is called when you do Create.() and where
27
- # all the fun starts, ends, and hopefully starts again.
28
- def call(options)
29
- pipe = self["pipetree"] # TODO: injectable? WTF? how cool is that?
30
-
31
- last, operation = pipe.(self, options)
32
-
33
- # Any subclass of Right will be interpreted as successful.
34
- Result.new(!!(last <= Railway::Right), options)
35
- end
36
-
37
- # This method would be redundant if Ruby had a Class::finalize! method the way
38
- # Dry.RB provides it. It has to be executed with every subclassing.
39
- def initialize_pipetree!
40
- heritage.record :initialize_pipetree!
41
-
42
- self["pipetree"] = Railway.new
43
-
44
- strut = ->(last, input, options) { [last, Instantiate.(input, options)] } # first step in pipe.
45
- self["pipetree"].add(Railway::Right, strut, name: "operation.new") # DISCUSS: using pipe API directly here. clever?
46
- end
47
- end
48
-
49
- class Railway < ::Pipetree::Railway
50
- FailFast = Class.new(Left)
51
- PassFast = Class.new(Right)
52
-
53
- def self.fail! ; Left end
54
- def self.fail_fast!; FailFast end
55
- def self.pass! ; Right end
56
- def self.pass_fast!; PassFast end
57
- end
58
-
59
- # The Strut wrapping each step. Makes sure that Track signals are returned immediately.
60
- class Switch < ::Pipetree::Railway::Strut
61
- Decider = ->(result, config, *args) do
62
- return result if result.is_a?(Class) && result <= Railway::Track # this might be pretty slow?
63
-
64
- config[:decider_class].(result, config, *args) # e.g. And::Decider.(result, ..)
65
- end
66
- end
67
-
68
- # Strut that doesn't evaluate the step's result but stays on `last` or configured :signal.
69
- class Stay < ::Pipetree::Railway::Strut
70
- Decider = ->(result, config, last, *) { config[:signal] || last }
71
- end
72
-
73
- module DSL
74
- def success(*args); add(Railway::Right, Stay::Decider, *args) end
75
- def failure(*args); add(Railway::Left, Stay::Decider, *args) end
76
- def step(*args) ; add(Railway::Right, Railway::And::Decider, *args) end
77
-
78
- private
79
- # Operation-level entry point.
80
- def add(track, decider_class, proc, options={})
81
- heritage.record(:add, track, decider_class, proc, options)
82
-
83
- DSL.insert(self["pipetree"], track, decider_class, proc, options)
84
- end
85
-
86
- def self.insert(pipe, track, decider_class, proc, options={}) # TODO: make :name required arg.
87
- _proc, options = proc.is_a?(Array) ? macro!(proc, options) : step!(proc, options)
88
-
89
- options = options.merge(replace: options[:name]) if options[:override] # :override
90
- strut_class, strut_options = AddOptions.(decider_class, options) # :fail_fast and friends.
91
-
92
- pipe.add(track, strut_class.new(_proc, strut_options), options)
93
- end
94
-
95
- def self.macro!(proc, options)
96
- _proc, macro_options = proc
97
-
98
- [ _proc, macro_options.merge(options) ]
99
- end
100
-
101
- def self.step!(proc, options)
102
- name = ""
103
- _proc = Option::KW.(proc) do |type|
104
- name = proc if type == :symbol
105
- name = "#{proc.source_location[0].split("/").last}:#{proc.source_location.last}" if proc.is_a? Proc if type == :proc
106
- name = proc.class if type == :callable
107
- end
108
-
109
- [ _proc, { name: name }.merge(options) ]
110
- end
111
-
112
- AddOptions = ->(decider_class, options) do
113
- # for #failure and #success:
114
- if decider_class == Stay::Decider
115
- return [Stay, signal: Railway::FailFast] if options[:fail_fast]
116
- return [Stay, signal: Railway::PassFast] if options[:pass_fast]
117
- return [Stay, {}]
118
- else # for #step:
119
- return [Switch, decider_class: decider_class, on_false: Railway::FailFast] if options[:fail_fast]
120
- return [Switch, decider_class: decider_class, on_true: Railway::PassFast] if options[:pass_fast]
121
- return [Switch, decider_class: decider_class]
122
- end
123
- end
124
- end # DSL
125
- end
126
-
127
- require "uber/callable"
128
- # Allows defining dependencies and inject/override them via runtime options, if desired.
129
- class Pipetree::Step
130
- include Uber::Callable
131
-
132
- def initialize(step, dependencies={})
133
- @step, @dependencies = step, dependencies
134
- end
135
-
136
- def call(input, options)
137
- @dependencies.each { |k, v| options[k] ||= v } # not sure i like this, but the step's API is cool.
138
-
139
- @step.(input, options)
140
- end
141
- end
142
- end